ruby_llm_community 1.3.0 → 1.3.1

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: b347c49cf17f54616f1a3dae46674509ab2a5809bc36522cf6d2275da8a3605d
4
- data.tar.gz: 7d80d568b98caeb73b4f36e6c6e610e2dac201b455cc270ea9001cbec5e1a839
3
+ metadata.gz: 729b5ec7255b0df4e787401cbdae2003e3be157cdf6d59ef1dbc94fdd6333a48
4
+ data.tar.gz: 0fd2092a50fe41c167d17b2b9c2fa9a06388336c6041faf92648c836f279756d
5
5
  SHA512:
6
- metadata.gz: 320df6f2d9250ea9df1fbea5db215927fe9ae5665344a3065fd2f185ae8b53f38e8ab4c7f174258cd290a6d8b5cabb8277a8c2a91b3d6d5f4e3094437a508fcd
7
- data.tar.gz: 92caeac605d923fd1c1eef4f5d8cc39f57e0972a33a57c09c518d3694da5202b0df7de5023135e9183931085bdfa5fadc76d8a354697ff11cbaa9255cc2b673c
6
+ metadata.gz: ec904259bdfdd81b996efd80054f65ecab1c3cb32d998c2b6f3bb9fccadce98fde58f3b7c9a3fbcb0ff26b71de5f918d35dbb6a9559a985471c4fb824d36196e
7
+ data.tar.gz: 835fed78e030f1f510251aed5a566545eaf8fc34b855fbe430ad7679ac349d6537742fc813aa648e6717593660dad1b3d2dde80a4c11e69bab8ea6b0c1386439
data/README.md CHANGED
@@ -36,8 +36,7 @@ Use this gem to get early access to features currently in PR at the main gem.
36
36
 
37
37
  Some examples --
38
38
 
39
- - Prompt caching for Anthropic
40
- - Image editing for Gemini and OpenAI
39
+ - Image editing for OpenAI
41
40
  - Responses API for OpenAI
42
41
  - xAI provider that supports Grok 2, Grok 3, Grok 4, and Grok Code
43
42
  - Red Candle provider
@@ -52,8 +52,10 @@ module RubyLLM
52
52
  def acts_as_chat_declaration
53
53
  params = []
54
54
 
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)
55
+ add_association_params(params, :messages, message_table_name, message_model_name,
56
+ owner_table: chat_table_name, plural: true)
57
+ add_association_params(params, :model, model_table_name, model_model_name,
58
+ owner_table: chat_table_name)
57
59
 
58
60
  "acts_as_chat#{" #{params.join(', ')}" if params.any?}"
59
61
  end
@@ -61,9 +63,12 @@ module RubyLLM
61
63
  def acts_as_message_declaration
62
64
  params = []
63
65
 
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)
66
+ add_association_params(params, :chat, chat_table_name, chat_model_name,
67
+ owner_table: message_table_name)
68
+ add_association_params(params, :tool_calls, tool_call_table_name, tool_call_model_name,
69
+ owner_table: message_table_name, plural: true)
70
+ add_association_params(params, :model, model_table_name, model_model_name,
71
+ owner_table: message_table_name)
67
72
 
68
73
  "acts_as_message#{" #{params.join(', ')}" if params.any?}"
69
74
  end
@@ -71,7 +76,8 @@ module RubyLLM
71
76
  def acts_as_model_declaration
72
77
  params = []
73
78
 
74
- add_association_params(params, :chats, chat_table_name, chat_model_name, plural: true)
79
+ add_association_params(params, :chats, chat_table_name, chat_model_name,
80
+ owner_table: model_table_name, plural: true)
75
81
 
76
82
  "acts_as_model#{" #{params.join(', ')}" if params.any?}"
77
83
  end
@@ -79,7 +85,8 @@ module RubyLLM
79
85
  def acts_as_tool_call_declaration
80
86
  params = []
81
87
 
82
- add_association_params(params, :message, message_table_name, message_model_name)
88
+ add_association_params(params, :message, message_table_name, message_model_name,
89
+ owner_table: tool_call_table_name)
83
90
 
84
91
  "acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
85
92
  end
@@ -120,6 +127,12 @@ module RubyLLM
120
127
  false
121
128
  end
122
129
 
130
+ def mysql?
131
+ ::ActiveRecord::Base.connection.adapter_name.downcase.include?('mysql')
132
+ rescue StandardError
133
+ false
134
+ end
135
+
123
136
  def table_exists?(table_name)
124
137
  ::ActiveRecord::Base.connection.table_exists?(table_name)
125
138
  rescue StandardError
@@ -128,13 +141,21 @@ module RubyLLM
128
141
 
129
142
  private
130
143
 
131
- def add_association_params(params, default_assoc, table_name, model_name, plural: false)
144
+ def add_association_params(params, default_assoc, table_name, model_name, owner_table:, plural: false) # rubocop:disable Metrics/ParameterLists
132
145
  assoc = plural ? table_name.to_sym : table_name.singularize.to_sym
133
146
 
134
- return if assoc == default_assoc
147
+ default_foreign_key = "#{default_assoc}_id"
148
+ # has_many/has_one: foreign key is on the associated table pointing back to owner
149
+ # belongs_to: foreign key is on the owner table pointing to associated table
150
+ foreign_key = if plural || default_assoc.to_s.pluralize == default_assoc.to_s # has_many or has_one
151
+ "#{owner_table.singularize}_id"
152
+ else # belongs_to
153
+ "#{table_name.singularize}_id"
154
+ end
135
155
 
136
- params << "#{default_assoc}: :#{assoc}"
156
+ params << "#{default_assoc}: :#{assoc}" if assoc != default_assoc
137
157
  params << "#{default_assoc.to_s.singularize}_class: '#{model_name}'" if model_name != assoc.to_s.classify
158
+ params << "#{default_assoc}_foreign_key: :#{foreign_key}" if foreign_key != default_foreign_key
138
159
  end
139
160
 
140
161
  # Convert namespaced model names to proper table names
@@ -14,6 +14,11 @@ class Create<%= model_model_name.gsub('::', '').pluralize %> < ActiveRecord::Mig
14
14
  t.jsonb :capabilities, default: []
15
15
  t.jsonb :pricing, default: {}
16
16
  t.jsonb :metadata, default: {}
17
+ <% elsif mysql? %>
18
+ t.json :modalities
19
+ t.json :capabilities
20
+ t.json :pricing
21
+ t.json :metadata
17
22
  <% else %>
18
23
  t.json :modalities, default: {}
19
24
  t.json :capabilities, default: []
@@ -4,7 +4,13 @@ class Create<%= tool_call_model_name.gsub('::', '').pluralize %> < ActiveRecord:
4
4
  create_table :<%= tool_call_table_name %> do |t|
5
5
  t.string :tool_call_id, null: false
6
6
  t.string :name, null: false
7
- t.<%= postgresql? ? 'jsonb' : 'json' %> :arguments, default: {}
7
+ <% if postgresql? %>
8
+ t.jsonb :arguments, default: {}
9
+ <% elsif mysql? %>
10
+ t.json :arguments
11
+ <% else %>
12
+ t.json :arguments, default: {}
13
+ <% end %>
8
14
  t.timestamps
9
15
  end
10
16
 
@@ -31,8 +31,8 @@ module RubyLLM
31
31
  end
32
32
 
33
33
  class_methods do # rubocop:disable Metrics/BlockLength
34
- def acts_as_chat(messages: :messages, message_class: nil,
35
- model: :model, model_class: nil)
34
+ def acts_as_chat(messages: :messages, message_class: nil, messages_foreign_key: nil, # rubocop:disable Metrics/ParameterLists
35
+ model: :model, model_class: nil, model_foreign_key: nil)
36
36
  include RubyLLM::ActiveRecord::ChatMethods
37
37
 
38
38
  class_attribute :messages_association_name, :model_association_name, :message_class, :model_class
@@ -45,12 +45,12 @@ module RubyLLM
45
45
  has_many messages,
46
46
  -> { order(created_at: :asc) },
47
47
  class_name: self.message_class,
48
- foreign_key: ActiveSupport::Inflector.foreign_key(table_name.singularize),
48
+ foreign_key: messages_foreign_key,
49
49
  dependent: :destroy
50
50
 
51
51
  belongs_to model,
52
52
  class_name: self.model_class,
53
- foreign_key: ActiveSupport::Inflector.foreign_key(model.to_s.singularize),
53
+ foreign_key: model_foreign_key,
54
54
  optional: true
55
55
 
56
56
  delegate :add_message, to: :to_llm
@@ -68,7 +68,7 @@ module RubyLLM
68
68
  end
69
69
  end
70
70
 
71
- def acts_as_model(chats: :chats, chat_class: nil)
71
+ def acts_as_model(chats: :chats, chat_class: nil, chats_foreign_key: nil)
72
72
  include RubyLLM::ActiveRecord::ModelMethods
73
73
 
74
74
  class_attribute :chats_association_name, :chat_class
@@ -80,18 +80,16 @@ module RubyLLM
80
80
  validates :provider, presence: true
81
81
  validates :name, presence: true
82
82
 
83
- has_many chats,
84
- class_name: self.chat_class,
85
- foreign_key: ActiveSupport::Inflector.foreign_key(table_name.singularize)
83
+ has_many chats, class_name: self.chat_class, foreign_key: chats_foreign_key
86
84
 
87
85
  define_method :chats_association do
88
86
  send(chats_association_name)
89
87
  end
90
88
  end
91
89
 
92
- def acts_as_message(chat: :chat, chat_class: nil, touch_chat: false, # rubocop:disable Metrics/ParameterLists
93
- tool_calls: :tool_calls, tool_call_class: nil,
94
- model: :model, model_class: nil)
90
+ def acts_as_message(chat: :chat, chat_class: nil, chat_foreign_key: nil, touch_chat: false, # rubocop:disable Metrics/ParameterLists
91
+ tool_calls: :tool_calls, tool_call_class: nil, tool_calls_foreign_key: nil,
92
+ model: :model, model_class: nil, model_foreign_key: nil)
95
93
  include RubyLLM::ActiveRecord::MessageMethods
96
94
 
97
95
  class_attribute :chat_association_name, :tool_calls_association_name, :model_association_name,
@@ -106,12 +104,12 @@ module RubyLLM
106
104
 
107
105
  belongs_to chat,
108
106
  class_name: self.chat_class,
109
- foreign_key: ActiveSupport::Inflector.foreign_key(chat.to_s.singularize),
107
+ foreign_key: chat_foreign_key,
110
108
  touch: touch_chat
111
109
 
112
110
  has_many tool_calls,
113
111
  class_name: self.tool_call_class,
114
- foreign_key: ActiveSupport::Inflector.foreign_key(table_name.singularize),
112
+ foreign_key: tool_calls_foreign_key,
115
113
  dependent: :destroy
116
114
 
117
115
  belongs_to :parent_tool_call,
@@ -126,7 +124,7 @@ module RubyLLM
126
124
 
127
125
  belongs_to model,
128
126
  class_name: self.model_class,
129
- foreign_key: ActiveSupport::Inflector.foreign_key(model.to_s.singularize),
127
+ foreign_key: model_foreign_key,
130
128
  optional: true
131
129
 
132
130
  delegate :tool_call?, :tool_result?, to: :to_llm
@@ -144,8 +142,8 @@ module RubyLLM
144
142
  end
145
143
  end
146
144
 
147
- def acts_as_tool_call(message: :message, message_class: nil,
148
- result: :result, result_class: nil)
145
+ def acts_as_tool_call(message: :message, message_class: nil, message_foreign_key: nil, # rubocop:disable Metrics/ParameterLists
146
+ result: :result, result_class: nil, result_foreign_key: nil)
149
147
  class_attribute :message_association_name, :result_association_name, :message_class, :result_class
150
148
 
151
149
  self.message_association_name = message
@@ -155,11 +153,11 @@ module RubyLLM
155
153
 
156
154
  belongs_to message,
157
155
  class_name: self.message_class,
158
- foreign_key: ActiveSupport::Inflector.foreign_key(message.to_s.singularize)
156
+ foreign_key: message_foreign_key
159
157
 
160
158
  has_one result,
161
159
  class_name: self.result_class,
162
- foreign_key: ActiveSupport::Inflector.foreign_key(table_name.singularize),
160
+ foreign_key: result_foreign_key,
163
161
  dependent: :nullify
164
162
 
165
163
  define_method :message_association do
@@ -11,11 +11,6 @@
11
11
  "claude-3-5-haiku-latest": {
12
12
  "anthropic": "claude-3-5-haiku-latest"
13
13
  },
14
- "claude-3-5-sonnet": {
15
- "anthropic": "claude-3-5-sonnet-20241022",
16
- "openrouter": "anthropic/claude-3.5-sonnet",
17
- "bedrock": "anthropic.claude-3-5-sonnet-20240620-v1:0:200k"
18
- },
19
14
  "claude-3-7-sonnet": {
20
15
  "anthropic": "claude-3-7-sonnet-20250219",
21
16
  "openrouter": "anthropic/claude-3.7-sonnet",
@@ -245,6 +240,22 @@
245
240
  "openai": "gpt-5-nano",
246
241
  "openrouter": "openai/gpt-5-nano"
247
242
  },
243
+ "gpt-5-pro": {
244
+ "openai": "gpt-5-pro",
245
+ "openrouter": "openai/gpt-5-pro"
246
+ },
247
+ "gpt-oss-120b": {
248
+ "openai": "gpt-oss-120b",
249
+ "openrouter": "openai/gpt-oss-120b"
250
+ },
251
+ "gpt-oss-20b": {
252
+ "openai": "gpt-oss-20b",
253
+ "openrouter": "openai/gpt-oss-20b"
254
+ },
255
+ "imagen-4.0-generate-001": {
256
+ "gemini": "imagen-4.0-generate-001",
257
+ "vertexai": "imagen-4.0-generate-001"
258
+ },
248
259
  "grok-2": {
249
260
  "xai": "grok-2-1212"
250
261
  },
@@ -323,14 +334,6 @@
323
334
  "openai": "o1",
324
335
  "openrouter": "openai/o1"
325
336
  },
326
- "o1-mini": {
327
- "openai": "o1-mini",
328
- "openrouter": "openai/o1-mini"
329
- },
330
- "o1-mini-2024-09-12": {
331
- "openai": "o1-mini-2024-09-12",
332
- "openrouter": "openai/o1-mini-2024-09-12"
333
- },
334
337
  "o1-pro": {
335
338
  "openai": "o1-pro",
336
339
  "openrouter": "openai/o1-pro"
@@ -7,17 +7,8 @@ module RubyLLM
7
7
 
8
8
  def initialize(source, filename: nil)
9
9
  @source = source
10
- if url?
11
- @source = URI source
12
- @filename = filename || File.basename(@source.path).to_s
13
- elsif path?
14
- @source = Pathname.new source
15
- @filename = filename || @source.basename.to_s
16
- elsif active_storage?
17
- @filename = filename || extract_filename_from_active_storage
18
- else
19
- @filename = filename
20
- end
10
+ @source = source_type_cast
11
+ @filename = filename || source_filename
21
12
 
22
13
  determine_mime_type
23
14
  end
@@ -168,6 +159,38 @@ module RubyLLM
168
159
  end
169
160
  end
170
161
 
162
+ def source_type_cast
163
+ if url?
164
+ URI(@source)
165
+ elsif path?
166
+ Pathname.new(@source)
167
+ else
168
+ @source
169
+ end
170
+ end
171
+
172
+ def source_filename
173
+ if url?
174
+ File.basename(@source.path).to_s
175
+ elsif path?
176
+ @source.basename.to_s
177
+ elsif io_like?
178
+ extract_filename_from_io
179
+ elsif active_storage?
180
+ extract_filename_from_active_storage
181
+ end
182
+ end
183
+
184
+ def extract_filename_from_io
185
+ if defined?(ActionDispatch::Http::UploadedFile) && @source.is_a?(ActionDispatch::Http::UploadedFile)
186
+ @source.original_filename.to_s
187
+ elsif @source.respond_to?(:path)
188
+ File.basename(@source.path).to_s
189
+ else
190
+ 'attachment'
191
+ end
192
+ end
193
+
171
194
  def extract_filename_from_active_storage # rubocop:disable Metrics/PerceivedComplexity
172
195
  return 'attachment' unless defined?(ActiveStorage)
173
196