ruby_llm 1.14.0 → 1.15.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -5
  3. data/lib/generators/ruby_llm/generator_helpers.rb +8 -0
  4. data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +1 -1
  5. data/lib/ruby_llm/active_record/acts_as.rb +3 -0
  6. data/lib/ruby_llm/active_record/acts_as_legacy.rb +52 -25
  7. data/lib/ruby_llm/active_record/chat_methods.rb +47 -23
  8. data/lib/ruby_llm/active_record/message_methods.rb +19 -14
  9. data/lib/ruby_llm/active_record/model_methods.rb +7 -9
  10. data/lib/ruby_llm/active_record/payload_helpers.rb +29 -0
  11. data/lib/ruby_llm/active_record/tool_call_methods.rb +5 -15
  12. data/lib/ruby_llm/agent.rb +3 -2
  13. data/lib/ruby_llm/aliases.json +53 -14
  14. data/lib/ruby_llm/attachment.rb +11 -27
  15. data/lib/ruby_llm/chat.rb +62 -21
  16. data/lib/ruby_llm/cost.rb +224 -0
  17. data/lib/ruby_llm/image.rb +37 -4
  18. data/lib/ruby_llm/message.rb +20 -0
  19. data/lib/ruby_llm/model/info.rb +17 -0
  20. data/lib/ruby_llm/model/pricing_category.rb +13 -2
  21. data/lib/ruby_llm/models.json +26511 -24930
  22. data/lib/ruby_llm/models.rb +2 -1
  23. data/lib/ruby_llm/models_schema.json +3 -0
  24. data/lib/ruby_llm/provider.rb +10 -3
  25. data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -133
  26. data/lib/ruby_llm/providers/anthropic/models.rb +2 -8
  27. data/lib/ruby_llm/providers/anthropic/tools.rb +4 -1
  28. data/lib/ruby_llm/providers/bedrock/chat.rb +24 -13
  29. data/lib/ruby_llm/providers/bedrock/streaming.rb +4 -1
  30. data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -119
  31. data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -215
  32. data/lib/ruby_llm/providers/gemini/chat.rb +8 -1
  33. data/lib/ruby_llm/providers/gemini/images.rb +2 -2
  34. data/lib/ruby_llm/providers/gemini/models.rb +2 -4
  35. data/lib/ruby_llm/providers/gemini/streaming.rb +4 -1
  36. data/lib/ruby_llm/providers/gemini/tools.rb +3 -1
  37. data/lib/ruby_llm/providers/mistral/capabilities.rb +6 -1
  38. data/lib/ruby_llm/providers/mistral/chat.rb +55 -4
  39. data/lib/ruby_llm/providers/openai/capabilities.rb +157 -195
  40. data/lib/ruby_llm/providers/openai/chat.rb +45 -6
  41. data/lib/ruby_llm/providers/openai/images.rb +58 -6
  42. data/lib/ruby_llm/providers/openai/models.rb +2 -4
  43. data/lib/ruby_llm/providers/openai/streaming.rb +5 -6
  44. data/lib/ruby_llm/providers/openrouter/chat.rb +30 -6
  45. data/lib/ruby_llm/providers/openrouter/images.rb +2 -2
  46. data/lib/ruby_llm/providers/openrouter/models.rb +1 -1
  47. data/lib/ruby_llm/providers/openrouter/streaming.rb +5 -6
  48. data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
  49. data/lib/ruby_llm/providers/perplexity/models.rb +12 -14
  50. data/lib/ruby_llm/railtie.rb +6 -0
  51. data/lib/ruby_llm/tokens.rb +8 -0
  52. data/lib/ruby_llm/tool.rb +24 -7
  53. data/lib/ruby_llm/version.rb +1 -1
  54. data/lib/ruby_llm.rb +2 -4
  55. data/lib/tasks/models.rake +13 -12
  56. metadata +21 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac485bae964a23af2c0a7ed48fa45fdb3207ad52d1ef6cbdca9b008b4c2429ba
4
- data.tar.gz: 7219680107ccf2af1bd7378b0e1b50b2eb328280e8e896cf8f9af7ac270c761a
3
+ metadata.gz: 3cfdd21451b6e3daac0463dfdb7655892967e5a4da6b00846f353809a9abe18a
4
+ data.tar.gz: e1505ddd57326601298bb8975c20049a195eb87babd62b08ac0e6340c9e653f6
5
5
  SHA512:
6
- metadata.gz: cfed1faf8354e9be7b39cfd44bd51596eaca75fb5d810bea7da72691ed7a7494a3ba779504acc20f8e92767be522bb3d6b77fab7db435ab91994b2db21255d2f
7
- data.tar.gz: 39b8ed657d27655a1f179fd46915da6acf81a8d0144ef2891bacb4f156311ab1be924d07018b9a095005864f3fb750a63a793d93db6a44de22c4c9bf6fab040f
6
+ metadata.gz: 7222e575d4f1e5cf620fdc37f4c255c7ededeee945d7b77fac4129c2947d6f78f7d5641b45ed39470180e5bf2dfd48b36e0a6c16579e4e4835564c48b6907920
7
+ data.tar.gz: ce3fb0f0e8b3665fafe56717cf8fcb10733008821633b024013d0f6e5d0bc90ef47f25b0109aa0e80ee3df4002e2f9b6a20cd98595031aa57235588cb4677052
data/README.md CHANGED
@@ -9,10 +9,10 @@
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) — *Your AI coworker*
11
11
 
12
- [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg?a=10)](https://badge.fury.io/rb/ruby_llm)
12
+ [![Gem Version](https://badge.fury.io/rb/ruby_llm.svg)](https://badge.fury.io/rb/ruby_llm)
13
13
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)
14
14
  [![Gem Downloads](https://img.shields.io/gem/dt/ruby_llm)](https://rubygems.org/gems/ruby_llm)
15
- [![codecov](https://codecov.io/gh/crmne/ruby_llm/branch/main/graph/badge.svg?a=2)](https://codecov.io/gh/crmne/ruby_llm)
15
+ [![codecov](https://codecov.io/gh/crmne/ruby_llm/branch/main/graph/badge.svg)](https://codecov.io/gh/crmne/ruby_llm)
16
16
 
17
17
  <a href="https://trendshift.io/repositories/13640" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13640" alt="crmne%2Fruby_llm | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
18
18
  </div>
@@ -24,6 +24,10 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
24
24
 
25
25
  Build chatbots, AI agents, RAG applications. Works with OpenAI, xAI, Anthropic, Google, AWS, local models, and any OpenAI-compatible API.
26
26
 
27
+ ## From zero to AI chat app in under two minutes
28
+
29
+ https://github.com/user-attachments/assets/65422091-9338-47da-a303-92b918bd1345
30
+
27
31
  ## Why RubyLLM?
28
32
 
29
33
  Every AI provider ships their own bloated client. Different APIs. Different response formats. Different conventions. It's exhausting.
@@ -82,9 +86,7 @@ RubyLLM.moderate "Check if this text is safe"
82
86
  ```ruby
83
87
  # Let AI use your code
84
88
  class Weather < RubyLLM::Tool
85
- description "Get current weather"
86
- param :latitude
87
- param :longitude
89
+ desc "Get current weather"
88
90
 
89
91
  def execute(latitude:, longitude:)
90
92
  url = "https://api.open-meteo.com/v1/forecast?latitude=#{latitude}&longitude=#{longitude}&current=temperature_2m,wind_speed_10m"
@@ -87,6 +87,7 @@ module RubyLLM
87
87
 
88
88
  add_association_params(params, :message, message_table_name, message_model_name,
89
89
  owner_table: tool_call_table_name, owner_model_name: tool_call_model_name)
90
+ add_result_foreign_key_param(params)
90
91
 
91
92
  "acts_as_tool_call#{" #{params.join(', ')}" if params.any?}"
92
93
  end
@@ -178,6 +179,13 @@ module RubyLLM
178
179
  "#{owner_model_name.demodulize.underscore}_id"
179
180
  end
180
181
 
182
+ def add_result_foreign_key_param(params)
183
+ foreign_key = "#{tool_call_table_name.singularize}_id"
184
+ default_foreign_key = "#{tool_call_model_name.demodulize.underscore}_id"
185
+
186
+ params << "result_foreign_key: :#{foreign_key}" if foreign_key != default_foreign_key
187
+ end
188
+
181
189
  # Convert namespaced model names to proper table names
182
190
  # e.g., "Assistant::Chat" -> "assistant_chats" (not "assistant/chats")
183
191
  def table_name_for(model_name)
@@ -1,5 +1,5 @@
1
1
  class <%= class_name %>Tool < RubyLLM::Tool
2
- description "TODO: describe what this tool does"
2
+ desc "TODO: describe what this tool does"
3
3
 
4
4
  def execute
5
5
  # TODO: return something to the LLM.
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+ require 'active_support/inflector'
5
+
3
6
  module RubyLLM
4
7
  module ActiveRecord
5
8
  # Adds chat and message persistence capabilities to ActiveRecord models.
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+ require 'active_support/inflector'
5
+
3
6
  module RubyLLM
4
7
  module ActiveRecord
5
8
  # Adds chat and message persistence capabilities to ActiveRecord models.
@@ -160,27 +163,33 @@ module RubyLLM
160
163
  self
161
164
  end
162
165
 
163
- def on_new_message(&block)
164
- to_llm
166
+ def on_new_message(&)
167
+ to_llm.on_new_message(&)
168
+ self
169
+ end
165
170
 
166
- existing_callback = @chat.instance_variable_get(:@on)[:new_message]
171
+ def on_end_message(&)
172
+ to_llm.on_end_message(&)
173
+ self
174
+ end
167
175
 
168
- @chat.on_new_message do
169
- existing_callback&.call
170
- block&.call
171
- end
176
+ def before_message(...)
177
+ to_llm.before_message(...)
172
178
  self
173
179
  end
174
180
 
175
- def on_end_message(&block)
176
- to_llm
181
+ def after_message(...)
182
+ to_llm.after_message(...)
183
+ self
184
+ end
177
185
 
178
- existing_callback = @chat.instance_variable_get(:@on)[:end_message]
186
+ def before_tool_call(...)
187
+ to_llm.before_tool_call(...)
188
+ self
189
+ end
179
190
 
180
- @chat.on_end_message do |msg|
181
- existing_callback&.call(msg)
182
- block&.call(msg)
183
- end
191
+ def after_tool_result(...)
192
+ to_llm.after_tool_result(...)
184
193
  self
185
194
  end
186
195
 
@@ -319,8 +328,8 @@ module RubyLLM
319
328
  def setup_persistence_callbacks
320
329
  return @chat if @chat.instance_variable_get(:@_persistence_callbacks_setup)
321
330
 
322
- @chat.on_new_message { persist_new_message }
323
- @chat.on_end_message { |msg| persist_message_completion(msg) }
331
+ @chat.before_message { persist_new_message }
332
+ @chat.after_message { |msg| persist_message_completion(msg) }
324
333
 
325
334
  @chat.instance_variable_set(:@_persistence_callbacks_setup, true)
326
335
  @chat
@@ -383,8 +392,8 @@ module RubyLLM
383
392
  case attachment
384
393
  when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob
385
394
  attachment
386
- when ActiveStorage::Attached::One, ActiveStorage::Attached::Many
387
- attachment.blobs
395
+ when ActiveStorage::Attachment, ActiveStorage::Attached::One, ActiveStorage::Attached::Many
396
+ active_storage_blobs(attachment)
388
397
  when Hash
389
398
  attachment.values.map { |v| prepare_for_active_storage(v) }
390
399
  else
@@ -398,16 +407,28 @@ module RubyLLM
398
407
 
399
408
  attachment = source.is_a?(RubyLLM::Attachment) ? source : RubyLLM::Attachment.new(source)
400
409
 
401
- {
402
- io: StringIO.new(attachment.content),
403
- filename: attachment.filename,
404
- content_type: attachment.mime_type
405
- }
410
+ if attachment.active_storage?
411
+ active_storage_blobs(attachment.source)
412
+ else
413
+ {
414
+ io: StringIO.new(attachment.content),
415
+ filename: attachment.filename,
416
+ content_type: attachment.mime_type
417
+ }
418
+ end
406
419
  rescue StandardError => e
407
420
  RubyLLM.logger.warn "Failed to process attachment #{source}: #{e.message}"
408
421
  nil
409
422
  end
410
423
 
424
+ def active_storage_blobs(attachment)
425
+ case attachment
426
+ when ActiveStorage::Blob then attachment
427
+ when ActiveStorage::Attachment, ActiveStorage::Attached::One then attachment.blob
428
+ when ActiveStorage::Attached::Many then attachment.blobs
429
+ end
430
+ end
431
+
411
432
  def build_content(message, attachments)
412
433
  return message if content_like?(message)
413
434
 
@@ -473,9 +494,15 @@ module RubyLLM
473
494
  end
474
495
 
475
496
  def extract_content
476
- return content unless respond_to?(:attachments) && attachments.attached?
497
+ text_content = if content.respond_to?(:to_plain_text)
498
+ content.to_plain_text
499
+ else
500
+ content
501
+ end
502
+
503
+ return text_content unless respond_to?(:attachments) && attachments.attached?
477
504
 
478
- RubyLLM::Content.new(content).tap do |content_obj|
505
+ RubyLLM::Content.new(text_content).tap do |content_obj|
479
506
  @_tempfiles = []
480
507
 
481
508
  attachments.each do |attachment|
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+
3
5
  module RubyLLM
4
6
  module ActiveRecord
5
7
  # Methods mixed into chat models.
@@ -154,27 +156,33 @@ module RubyLLM
154
156
  self
155
157
  end
156
158
 
157
- def on_new_message(&block)
158
- to_llm
159
+ def on_new_message(&)
160
+ to_llm.on_new_message(&)
161
+ self
162
+ end
159
163
 
160
- existing_callback = @chat.instance_variable_get(:@on)[:new_message]
164
+ def on_end_message(&)
165
+ to_llm.on_end_message(&)
166
+ self
167
+ end
161
168
 
162
- @chat.on_new_message do
163
- existing_callback&.call
164
- block&.call
165
- end
169
+ def before_message(...)
170
+ to_llm.before_message(...)
166
171
  self
167
172
  end
168
173
 
169
- def on_end_message(&block)
170
- to_llm
174
+ def after_message(...)
175
+ to_llm.after_message(...)
176
+ self
177
+ end
171
178
 
172
- existing_callback = @chat.instance_variable_get(:@on)[:end_message]
179
+ def before_tool_call(...)
180
+ to_llm.before_tool_call(...)
181
+ self
182
+ end
173
183
 
174
- @chat.on_end_message do |msg|
175
- existing_callback&.call(msg)
176
- block&.call(msg)
177
- end
184
+ def after_tool_result(...)
185
+ to_llm.after_tool_result(...)
178
186
  self
179
187
  end
180
188
 
@@ -208,6 +216,10 @@ module RubyLLM
208
216
  message_record
209
217
  end
210
218
 
219
+ def cost
220
+ RubyLLM::Cost.aggregate(messages_association.map(&:cost))
221
+ end
222
+
211
223
  def create_user_message(content, with: nil)
212
224
  add_message(role: :user, content: build_content(content, with))
213
225
  end
@@ -258,8 +270,8 @@ module RubyLLM
258
270
  def setup_persistence_callbacks
259
271
  return @chat if @chat.instance_variable_get(:@_persistence_callbacks_setup)
260
272
 
261
- @chat.on_new_message { persist_new_message }
262
- @chat.on_end_message { |msg| persist_message_completion(msg) }
273
+ @chat.before_message { persist_new_message }
274
+ @chat.after_message { |msg| persist_message_completion(msg) }
263
275
 
264
276
  @chat.instance_variable_set(:@_persistence_callbacks_setup, true)
265
277
  @chat
@@ -402,8 +414,8 @@ module RubyLLM
402
414
  case attachment
403
415
  when ActionDispatch::Http::UploadedFile, ActiveStorage::Blob
404
416
  attachment
405
- when ActiveStorage::Attached::One, ActiveStorage::Attached::Many
406
- attachment.blobs
417
+ when ActiveStorage::Attachment, ActiveStorage::Attached::One, ActiveStorage::Attached::Many
418
+ active_storage_blobs(attachment)
407
419
  when Hash
408
420
  attachment.values.map { |v| prepare_for_active_storage(v) }
409
421
  else
@@ -417,16 +429,28 @@ module RubyLLM
417
429
 
418
430
  attachment = source.is_a?(RubyLLM::Attachment) ? source : RubyLLM::Attachment.new(source)
419
431
 
420
- {
421
- io: StringIO.new(attachment.content),
422
- filename: attachment.filename,
423
- content_type: attachment.mime_type
424
- }
432
+ if attachment.active_storage?
433
+ active_storage_blobs(attachment.source)
434
+ else
435
+ {
436
+ io: StringIO.new(attachment.content),
437
+ filename: attachment.filename,
438
+ content_type: attachment.mime_type
439
+ }
440
+ end
425
441
  rescue StandardError => e
426
442
  RubyLLM.logger.warn "Failed to process attachment #{source}: #{e.message}"
427
443
  nil
428
444
  end
429
445
 
446
+ def active_storage_blobs(attachment)
447
+ case attachment
448
+ when ActiveStorage::Blob then attachment
449
+ when ActiveStorage::Attachment, ActiveStorage::Attached::One then attachment.blob
450
+ when ActiveStorage::Attached::Many then attachment.blobs
451
+ end
452
+ end
453
+
430
454
  def build_content(message, attachments)
431
455
  return message if content_like?(message)
432
456
 
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+ require 'ruby_llm/active_record/payload_helpers'
5
+
3
6
  module RubyLLM
4
7
  module ActiveRecord
5
8
  # Methods mixed into message models.
6
9
  module MessageMethods
7
10
  extend ActiveSupport::Concern
11
+ include PayloadHelpers
8
12
 
9
13
  class_methods do
10
14
  attr_reader :chat_class, :tool_call_class, :chat_foreign_key, :tool_call_foreign_key
@@ -39,6 +43,18 @@ module RubyLLM
39
43
  )
40
44
  end
41
45
 
46
+ def cost
47
+ RubyLLM::Cost.new(tokens:, model: model_association)
48
+ end
49
+
50
+ def cache_read_tokens
51
+ cached_value
52
+ end
53
+
54
+ def cache_write_tokens
55
+ cache_creation_value
56
+ end
57
+
42
58
  def to_partial_path
43
59
  partial_prefix = self.class.name.underscore.pluralize
44
60
  role_partial = if to_llm.tool_call?
@@ -52,10 +68,7 @@ module RubyLLM
52
68
  end
53
69
 
54
70
  def tool_error_message
55
- payload = parse_payload(content)
56
- return unless payload.is_a?(Hash)
57
-
58
- payload['error'] || payload[:error]
71
+ payload_error_message(content)
59
72
  end
60
73
 
61
74
  private
@@ -101,7 +114,8 @@ module RubyLLM
101
114
  def extract_content
102
115
  return RubyLLM::Content::Raw.new(content_raw) if has_attribute?(:content_raw) && content_raw.present?
103
116
 
104
- content_value = self[:content]
117
+ content_value = content
118
+ content_value = content_value.to_plain_text if content_value.respond_to?(:to_plain_text)
105
119
 
106
120
  return content_value unless respond_to?(:attachments) && attachments.attached?
107
121
 
@@ -128,15 +142,6 @@ module RubyLLM
128
142
  @_tempfiles << tempfile
129
143
  tempfile
130
144
  end
131
-
132
- def parse_payload(value)
133
- return value if value.is_a?(Hash) || value.is_a?(Array)
134
- return if value.blank?
135
-
136
- JSON.parse(value)
137
- rescue JSON::ParserError
138
- nil
139
- end
140
145
  end
141
146
  end
142
147
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/module/delegation'
5
+
3
6
  module RubyLLM
4
7
  module ActiveRecord
5
8
  # Methods mixed into model registry models.
@@ -10,15 +13,7 @@ module RubyLLM
10
13
  def refresh!
11
14
  RubyLLM.models.refresh!
12
15
 
13
- transaction do
14
- RubyLLM.models.all.each do |model_info|
15
- model = find_or_initialize_by(
16
- model_id: model_info.id,
17
- provider: model_info.provider
18
- )
19
- model.update!(from_llm_attributes(model_info))
20
- end
21
- end
16
+ save_to_database
22
17
  end
23
18
 
24
19
  def save_to_database
@@ -76,8 +71,11 @@ module RubyLLM
76
71
 
77
72
  delegate :supports?, :supports_vision?, :supports_functions?, :type,
78
73
  :input_price_per_million, :output_price_per_million,
74
+ :cache_read_input_price_per_million, :cache_write_input_price_per_million,
75
+ :cached_input_price_per_million, :cache_creation_input_price_per_million,
79
76
  :function_calling?, :structured_output?, :batch?,
80
77
  :reasoning?, :citations?, :streaming?, :provider_class, :label,
78
+ :cost_for,
81
79
  to: :to_llm
82
80
  end
83
81
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'json'
5
+
6
+ module RubyLLM
7
+ module ActiveRecord
8
+ # Shared helpers for parsing serialized payloads on ActiveRecord-backed models.
9
+ module PayloadHelpers
10
+ private
11
+
12
+ def payload_error_message(value)
13
+ payload = parse_payload(value)
14
+ return unless payload.is_a?(Hash)
15
+
16
+ payload['error'] || payload[:error]
17
+ end
18
+
19
+ def parse_payload(value)
20
+ return value if value.is_a?(Hash) || value.is_a?(Array)
21
+ return if value.blank?
22
+
23
+ JSON.parse(value)
24
+ rescue JSON::ParserError
25
+ nil
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,27 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/concern'
4
+ require 'ruby_llm/active_record/payload_helpers'
5
+
3
6
  module RubyLLM
4
7
  module ActiveRecord
5
8
  # Methods mixed into tool call models.
6
9
  module ToolCallMethods
7
10
  extend ActiveSupport::Concern
11
+ include PayloadHelpers
8
12
 
9
13
  def tool_error_message
10
- payload = parse_payload(arguments)
11
- return unless payload.is_a?(Hash)
12
-
13
- payload['error'] || payload[:error]
14
- end
15
-
16
- private
17
-
18
- def parse_payload(value)
19
- return value if value.is_a?(Hash) || value.is_a?(Array)
20
- return if value.blank?
21
-
22
- JSON.parse(value)
23
- rescue JSON::ParserError
24
- nil
14
+ payload_error_message(arguments)
25
15
  end
26
16
  end
27
17
  end
@@ -359,7 +359,8 @@ module RubyLLM
359
359
 
360
360
  def_delegators :chat, :model, :messages, :tools, :params, :headers, :schema, :ask, :say, :with_tool, :with_tools,
361
361
  :with_model, :with_temperature, :with_thinking, :with_context, :with_params, :with_headers,
362
- :with_schema, :on_new_message, :on_end_message, :on_tool_call, :on_tool_result, :each, :complete,
363
- :add_message, :reset_messages!
362
+ :with_schema, :on_new_message, :on_end_message, :on_tool_call, :on_tool_result, :before_message,
363
+ :after_message, :before_tool_call, :after_tool_result, :each, :complete, :add_message,
364
+ :reset_messages!, :cost
364
365
  end
365
366
  end
@@ -9,17 +9,13 @@
9
9
  },
10
10
  "claude-3-5-sonnet": {
11
11
  "anthropic": "claude-3-5-sonnet-20241022",
12
- "openrouter": "anthropic/claude-3.5-sonnet",
13
- "bedrock": "anthropic.claude-3-5-sonnet-20240620-v1:0:200k"
12
+ "bedrock": "anthropic.claude-3-5-sonnet-20241022-v2:0"
14
13
  },
15
14
  "claude-3-7-sonnet": {
16
15
  "anthropic": "claude-3-7-sonnet-20250219",
17
16
  "openrouter": "anthropic/claude-3.7-sonnet",
18
17
  "bedrock": "anthropic.claude-3-7-sonnet-20250219-v1:0"
19
18
  },
20
- "claude-3-7-sonnet-latest": {
21
- "anthropic": "claude-3-7-sonnet-latest"
22
- },
23
19
  "claude-3-haiku": {
24
20
  "anthropic": "claude-3-haiku-20240307",
25
21
  "openrouter": "anthropic/claude-3-haiku",
@@ -64,6 +60,12 @@
64
60
  "bedrock": "anthropic.claude-opus-4-6-v1",
65
61
  "azure": "claude-opus-4-6"
66
62
  },
63
+ "claude-opus-4-7": {
64
+ "anthropic": "claude-opus-4-7",
65
+ "openrouter": "anthropic/claude-opus-4.7",
66
+ "bedrock": "anthropic.claude-opus-4-7",
67
+ "azure": "claude-opus-4-7"
68
+ },
67
69
  "claude-sonnet-4": {
68
70
  "anthropic": "claude-sonnet-4-20250514",
69
71
  "openrouter": "anthropic/claude-sonnet-4",
@@ -88,6 +90,14 @@
88
90
  "deepseek": "deepseek-chat",
89
91
  "openrouter": "deepseek/deepseek-chat"
90
92
  },
93
+ "deepseek-v4-flash": {
94
+ "deepseek": "deepseek-v4-flash",
95
+ "openrouter": "deepseek/deepseek-v4-flash"
96
+ },
97
+ "deepseek-v4-pro": {
98
+ "deepseek": "deepseek-v4-pro",
99
+ "openrouter": "deepseek/deepseek-v4-pro"
100
+ },
91
101
  "gemini-1.5-flash": {
92
102
  "gemini": "gemini-1.5-flash",
93
103
  "vertexai": "gemini-1.5-flash"
@@ -190,7 +200,8 @@
190
200
  },
191
201
  "gemini-3.1-flash-lite-preview": {
192
202
  "gemini": "gemini-3.1-flash-lite-preview",
193
- "openrouter": "google/gemini-3.1-flash-lite-preview"
203
+ "openrouter": "google/gemini-3.1-flash-lite-preview",
204
+ "vertexai": "gemini-3.1-flash-lite-preview"
194
205
  },
195
206
  "gemini-3.1-pro-preview": {
196
207
  "gemini": "gemini-3.1-pro-preview",
@@ -206,6 +217,10 @@
206
217
  "gemini": "gemini-embedding-001",
207
218
  "vertexai": "gemini-embedding-001"
208
219
  },
220
+ "gemini-embedding-2": {
221
+ "gemini": "gemini-embedding-2",
222
+ "vertexai": "gemini-embedding-2"
223
+ },
209
224
  "gemini-flash": {
210
225
  "gemini": "gemini-flash-latest",
211
226
  "vertexai": "gemini-flash-latest"
@@ -230,6 +245,14 @@
230
245
  "gemini": "gemma-3n-e4b-it",
231
246
  "openrouter": "google/gemma-3n-e4b-it"
232
247
  },
248
+ "gemma-4-26b-a4b-it": {
249
+ "gemini": "gemma-4-26b-a4b-it",
250
+ "openrouter": "google/gemma-4-26b-a4b-it"
251
+ },
252
+ "gemma-4-31b-it": {
253
+ "gemini": "gemma-4-31b-it",
254
+ "openrouter": "google/gemma-4-31b-it"
255
+ },
233
256
  "gpt-3.5-turbo": {
234
257
  "openai": "gpt-3.5-turbo",
235
258
  "openrouter": "openai/gpt-3.5-turbo"
@@ -247,18 +270,10 @@
247
270
  "openrouter": "openai/gpt-4",
248
271
  "azure": "gpt-4"
249
272
  },
250
- "gpt-4-1106-preview": {
251
- "openai": "gpt-4-1106-preview",
252
- "openrouter": "openai/gpt-4-1106-preview"
253
- },
254
273
  "gpt-4-turbo": {
255
274
  "openai": "gpt-4-turbo",
256
275
  "openrouter": "openai/gpt-4-turbo"
257
276
  },
258
- "gpt-4-turbo-preview": {
259
- "openai": "gpt-4-turbo-preview",
260
- "openrouter": "openai/gpt-4-turbo-preview"
261
- },
262
277
  "gpt-4.1": {
263
278
  "openai": "gpt-4.1",
264
279
  "openrouter": "openai/gpt-4.1",
@@ -373,10 +388,26 @@
373
388
  "openai": "gpt-5.4",
374
389
  "openrouter": "openai/gpt-5.4"
375
390
  },
391
+ "gpt-5.4-mini": {
392
+ "openai": "gpt-5.4-mini",
393
+ "openrouter": "openai/gpt-5.4-mini"
394
+ },
395
+ "gpt-5.4-nano": {
396
+ "openai": "gpt-5.4-nano",
397
+ "openrouter": "openai/gpt-5.4-nano"
398
+ },
376
399
  "gpt-5.4-pro": {
377
400
  "openai": "gpt-5.4-pro",
378
401
  "openrouter": "openai/gpt-5.4-pro"
379
402
  },
403
+ "gpt-5.5": {
404
+ "openai": "gpt-5.5",
405
+ "openrouter": "openai/gpt-5.5"
406
+ },
407
+ "gpt-5.5-pro": {
408
+ "openai": "gpt-5.5-pro",
409
+ "openrouter": "openai/gpt-5.5-pro"
410
+ },
380
411
  "gpt-audio": {
381
412
  "openai": "gpt-audio",
382
413
  "openrouter": "openai/gpt-audio"
@@ -385,6 +416,14 @@
385
416
  "openai": "gpt-audio-mini",
386
417
  "openrouter": "openai/gpt-audio-mini"
387
418
  },
419
+ "lyria-3-clip-preview": {
420
+ "gemini": "lyria-3-clip-preview",
421
+ "openrouter": "google/lyria-3-clip-preview"
422
+ },
423
+ "lyria-3-pro-preview": {
424
+ "gemini": "lyria-3-pro-preview",
425
+ "openrouter": "google/lyria-3-pro-preview"
426
+ },
388
427
  "o1": {
389
428
  "openai": "o1",
390
429
  "openrouter": "openai/o1"