dify_llm 1.8.2 → 1.9.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 +8 -3
- data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +3 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +1 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +15 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +49 -0
- data/lib/ruby_llm/active_record/acts_as.rb +6 -6
- data/lib/ruby_llm/active_record/chat_methods.rb +41 -13
- data/lib/ruby_llm/active_record/message_methods.rb +11 -2
- data/lib/ruby_llm/active_record/model_methods.rb +1 -1
- data/lib/ruby_llm/aliases.json +62 -20
- data/lib/ruby_llm/attachment.rb +8 -0
- data/lib/ruby_llm/chat.rb +13 -2
- data/lib/ruby_llm/configuration.rb +6 -1
- data/lib/ruby_llm/connection.rb +3 -3
- data/lib/ruby_llm/content.rb +23 -0
- data/lib/ruby_llm/message.rb +11 -6
- data/lib/ruby_llm/model/info.rb +4 -0
- data/lib/ruby_llm/models.json +9410 -7793
- data/lib/ruby_llm/models.rb +14 -22
- data/lib/ruby_llm/provider.rb +23 -1
- data/lib/ruby_llm/providers/anthropic/chat.rb +22 -3
- data/lib/ruby_llm/providers/anthropic/content.rb +44 -0
- data/lib/ruby_llm/providers/anthropic/media.rb +2 -1
- data/lib/ruby_llm/providers/anthropic/models.rb +15 -0
- data/lib/ruby_llm/providers/anthropic/streaming.rb +2 -0
- data/lib/ruby_llm/providers/anthropic/tools.rb +20 -18
- data/lib/ruby_llm/providers/bedrock/media.rb +2 -1
- data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +15 -0
- data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +2 -0
- data/lib/ruby_llm/providers/dify/chat.rb +16 -5
- data/lib/ruby_llm/providers/gemini/chat.rb +352 -69
- data/lib/ruby_llm/providers/gemini/media.rb +59 -1
- data/lib/ruby_llm/providers/gemini/tools.rb +146 -25
- data/lib/ruby_llm/providers/gemini/transcription.rb +116 -0
- data/lib/ruby_llm/providers/gemini.rb +2 -1
- data/lib/ruby_llm/providers/gpustack/media.rb +1 -0
- data/lib/ruby_llm/providers/ollama/media.rb +1 -0
- data/lib/ruby_llm/providers/openai/chat.rb +7 -2
- data/lib/ruby_llm/providers/openai/media.rb +2 -1
- data/lib/ruby_llm/providers/openai/streaming.rb +7 -2
- data/lib/ruby_llm/providers/openai/tools.rb +26 -6
- data/lib/ruby_llm/providers/openai/transcription.rb +70 -0
- data/lib/ruby_llm/providers/openai.rb +1 -0
- data/lib/ruby_llm/providers/vertexai/transcription.rb +16 -0
- data/lib/ruby_llm/providers/vertexai.rb +3 -0
- data/lib/ruby_llm/stream_accumulator.rb +10 -4
- data/lib/ruby_llm/tool.rb +126 -0
- data/lib/ruby_llm/transcription.rb +35 -0
- data/lib/ruby_llm/utils.rb +46 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +6 -0
- metadata +24 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 75dbb53612d3fa2c1089038bcf48fbbc0fe9425d37ffd8fccdfa56337daf97af
|
|
4
|
+
data.tar.gz: 316d3ef004a7387a6e723a02f8ab09729b167097127f9cb85ce5a864e6e4ef1e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bca45bf0d49f6e98e9ea00cf2e760537fbff5f861394080f8dec8baa634784289f23779476eedbda5c9d11e4323e2a67f6eadc84144c18cd4adc7d09e3b9cfe7
|
|
7
|
+
data.tar.gz: a12a868701ae4e70f6f397de290d3bc018da91993950366d956fd8d60f0c37cc8ef89f882a680db396336258107e032f881817224668b81d9d65b3f53c1f44d9
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
20
20
|
> [!NOTE]
|
|
21
|
-
> Using RubyLLM
|
|
21
|
+
> Using RubyLLM? [Share your story](https://tally.so/r/3Na02p)! Takes 5 minutes.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -69,6 +69,11 @@ RubyLLM.paint "a sunset over mountains in watercolor style"
|
|
|
69
69
|
RubyLLM.embed "Ruby is elegant and expressive"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
```ruby
|
|
73
|
+
# Transcribe audio to text
|
|
74
|
+
RubyLLM.transcribe "meeting.wav"
|
|
75
|
+
```
|
|
76
|
+
|
|
72
77
|
```ruby
|
|
73
78
|
# Moderate content for safety
|
|
74
79
|
RubyLLM.moderate "Check if this text is safe"
|
|
@@ -107,10 +112,10 @@ response = chat.with_schema(ProductSchema).ask "Analyze this product", with: "pr
|
|
|
107
112
|
|
|
108
113
|
* **Chat:** Conversational AI with `RubyLLM.chat`
|
|
109
114
|
* **Vision:** Analyze images and videos
|
|
110
|
-
* **Audio:** Transcribe and understand speech
|
|
115
|
+
* **Audio:** Transcribe and understand speech with `RubyLLM.transcribe`
|
|
111
116
|
* **Documents:** Extract from PDFs, CSVs, JSON, any file type
|
|
112
117
|
* **Image generation:** Create images with `RubyLLM.paint`
|
|
113
|
-
* **Embeddings:**
|
|
118
|
+
* **Embeddings:** Generate embeddings with `RubyLLM.embed`
|
|
114
119
|
* **Moderation:** Content safety with `RubyLLM.moderate`
|
|
115
120
|
* **Tools:** Let AI call your Ruby methods
|
|
116
121
|
* **Structured output:** JSON schemas that just work
|
|
@@ -3,8 +3,11 @@ class Create<%= message_model_name.gsub('::', '').pluralize %> < ActiveRecord::M
|
|
|
3
3
|
create_table :<%= message_table_name %> do |t|
|
|
4
4
|
t.string :role, null: false
|
|
5
5
|
t.text :content
|
|
6
|
+
t.json :content_raw
|
|
6
7
|
t.integer :input_tokens
|
|
7
8
|
t.integer :output_tokens
|
|
9
|
+
t.integer :cached_tokens
|
|
10
|
+
t.integer :cache_creation_tokens
|
|
8
11
|
t.timestamps
|
|
9
12
|
end
|
|
10
13
|
|
|
@@ -9,7 +9,7 @@ module RubyLLM
|
|
|
9
9
|
# Generator to upgrade existing RubyLLM apps to v1.7 with new Rails-like API
|
|
10
10
|
class UpgradeToV17Generator < Rails::Generators::Base
|
|
11
11
|
include Rails::Generators::Migration
|
|
12
|
-
include RubyLLM::GeneratorHelpers
|
|
12
|
+
include RubyLLM::Generators::GeneratorHelpers
|
|
13
13
|
|
|
14
14
|
namespace 'ruby_llm:upgrade_to_v1_7'
|
|
15
15
|
source_root File.expand_path('templates', __dir__)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class AddRubyLlmV19Columns < ActiveRecord::Migration<%= migration_version %>
|
|
2
|
+
def change
|
|
3
|
+
unless column_exists?(:<%= message_table_name %>, :cached_tokens)
|
|
4
|
+
add_column :<%= message_table_name %>, :cached_tokens, :integer
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
unless column_exists?(:<%= message_table_name %>, :cache_creation_tokens)
|
|
8
|
+
add_column :<%= message_table_name %>, :cache_creation_tokens, :integer
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
unless column_exists?(:<%= message_table_name %>, :content_raw)
|
|
12
|
+
add_column :<%= message_table_name %>, :content_raw, :json
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'rails/generators/active_record'
|
|
5
|
+
require_relative '../generator_helpers'
|
|
6
|
+
|
|
7
|
+
module RubyLLM
|
|
8
|
+
module Generators
|
|
9
|
+
# Generator to add v1.9 columns (cached tokens + raw content support) to existing apps.
|
|
10
|
+
class UpgradeToV19Generator < Rails::Generators::Base
|
|
11
|
+
include Rails::Generators::Migration
|
|
12
|
+
include RubyLLM::Generators::GeneratorHelpers
|
|
13
|
+
|
|
14
|
+
namespace 'ruby_llm:upgrade_to_v1_9'
|
|
15
|
+
source_root File.expand_path('templates', __dir__)
|
|
16
|
+
|
|
17
|
+
argument :model_mappings, type: :array, default: [], banner: 'message:MessageName'
|
|
18
|
+
|
|
19
|
+
desc 'Adds cached token columns and raw content storage fields introduced in v1.9.0'
|
|
20
|
+
|
|
21
|
+
def self.next_migration_number(dirname)
|
|
22
|
+
::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def create_migration_file
|
|
26
|
+
parse_model_mappings
|
|
27
|
+
|
|
28
|
+
migration_template 'add_v1_9_message_columns.rb.tt',
|
|
29
|
+
'db/migrate/add_ruby_llm_v1_9_columns.rb',
|
|
30
|
+
migration_version: migration_version,
|
|
31
|
+
message_table_name: message_table_name
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def show_next_steps
|
|
35
|
+
say_status :success, 'Upgrade prepared!', :green
|
|
36
|
+
say <<~INSTRUCTIONS
|
|
37
|
+
|
|
38
|
+
Next steps:
|
|
39
|
+
1. Review the generated migration
|
|
40
|
+
2. Run: rails db:migrate
|
|
41
|
+
3. Restart your application server
|
|
42
|
+
|
|
43
|
+
📚 See the v1.9.0 release notes for details on cached token tracking and raw content support.
|
|
44
|
+
|
|
45
|
+
INSTRUCTIONS
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -11,22 +11,22 @@ module RubyLLM
|
|
|
11
11
|
super
|
|
12
12
|
# Monkey-patch Models to use database when ActsAs is active
|
|
13
13
|
RubyLLM::Models.class_eval do
|
|
14
|
-
def load_models
|
|
14
|
+
def self.load_models
|
|
15
15
|
read_from_database
|
|
16
16
|
rescue StandardError => e
|
|
17
17
|
RubyLLM.logger.debug "Failed to load models from database: #{e.message}, falling back to JSON"
|
|
18
18
|
read_from_json
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def
|
|
22
|
-
@models = read_from_database
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def read_from_database
|
|
21
|
+
def self.read_from_database
|
|
26
22
|
model_class = RubyLLM.config.model_registry_class
|
|
27
23
|
model_class = model_class.constantize if model_class.is_a?(String)
|
|
28
24
|
model_class.all.map(&:to_llm)
|
|
29
25
|
end
|
|
26
|
+
|
|
27
|
+
def load_from_database!
|
|
28
|
+
@models = self.class.read_from_database
|
|
29
|
+
end
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -174,8 +174,16 @@ module RubyLLM
|
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
def create_user_message(content, with: nil)
|
|
177
|
-
|
|
177
|
+
content_text, attachments, content_raw = prepare_content_for_storage(content)
|
|
178
|
+
|
|
179
|
+
message_record = messages_association.build(role: :user)
|
|
180
|
+
message_record.content = content_text
|
|
181
|
+
message_record.content_raw = content_raw if message_record.respond_to?(:content_raw=)
|
|
182
|
+
message_record.save!
|
|
183
|
+
|
|
178
184
|
persist_content(message_record, with) if with.present?
|
|
185
|
+
persist_content(message_record, attachments) if attachments.present?
|
|
186
|
+
|
|
179
187
|
message_record
|
|
180
188
|
end
|
|
181
189
|
|
|
@@ -235,28 +243,25 @@ module RubyLLM
|
|
|
235
243
|
@message = messages_association.create!(role: :assistant, content: '')
|
|
236
244
|
end
|
|
237
245
|
|
|
238
|
-
|
|
246
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
247
|
+
def persist_message_completion(message)
|
|
239
248
|
return unless message
|
|
240
249
|
|
|
241
250
|
tool_call_id = find_tool_call_id(message.tool_call_id) if message.tool_call_id
|
|
242
251
|
|
|
243
252
|
transaction do
|
|
244
|
-
|
|
245
|
-
attachments_to_persist = nil
|
|
246
|
-
|
|
247
|
-
if content.is_a?(RubyLLM::Content)
|
|
248
|
-
attachments_to_persist = content.attachments if content.attachments.any?
|
|
249
|
-
content = content.text
|
|
250
|
-
elsif content.is_a?(Hash) || content.is_a?(Array)
|
|
251
|
-
content = content.to_json
|
|
252
|
-
end
|
|
253
|
+
content_text, attachments_to_persist, content_raw = prepare_content_for_storage(message.content)
|
|
253
254
|
|
|
254
255
|
attrs = {
|
|
255
256
|
role: message.role,
|
|
256
|
-
content:
|
|
257
|
+
content: content_text,
|
|
257
258
|
input_tokens: message.input_tokens,
|
|
258
259
|
output_tokens: message.output_tokens
|
|
259
260
|
}
|
|
261
|
+
attrs[:cached_tokens] = message.cached_tokens if @message.has_attribute?(:cached_tokens)
|
|
262
|
+
if @message.has_attribute?(:cache_creation_tokens)
|
|
263
|
+
attrs[:cache_creation_tokens] = message.cache_creation_tokens
|
|
264
|
+
end
|
|
260
265
|
|
|
261
266
|
# Add model association dynamically
|
|
262
267
|
attrs[self.class.model_association_name] = model_association
|
|
@@ -266,12 +271,15 @@ module RubyLLM
|
|
|
266
271
|
attrs[parent_tool_call_assoc.foreign_key] = tool_call_id
|
|
267
272
|
end
|
|
268
273
|
|
|
269
|
-
@message.
|
|
274
|
+
@message.assign_attributes(attrs)
|
|
275
|
+
@message.content_raw = content_raw if @message.respond_to?(:content_raw=)
|
|
276
|
+
@message.save!
|
|
270
277
|
|
|
271
278
|
persist_content(@message, attachments_to_persist) if attachments_to_persist
|
|
272
279
|
persist_tool_calls(message.tool_calls) if message.tool_calls.present?
|
|
273
280
|
end
|
|
274
281
|
end
|
|
282
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
275
283
|
|
|
276
284
|
def persist_tool_calls(tool_calls)
|
|
277
285
|
tool_calls.each_value do |tool_call|
|
|
@@ -331,6 +339,26 @@ module RubyLLM
|
|
|
331
339
|
RubyLLM.logger.warn "Failed to process attachment #{source}: #{e.message}"
|
|
332
340
|
nil
|
|
333
341
|
end
|
|
342
|
+
|
|
343
|
+
def prepare_content_for_storage(content)
|
|
344
|
+
attachments = nil
|
|
345
|
+
content_raw = nil
|
|
346
|
+
content_text = content
|
|
347
|
+
|
|
348
|
+
case content
|
|
349
|
+
when RubyLLM::Content::Raw
|
|
350
|
+
content_raw = content.value
|
|
351
|
+
content_text = nil
|
|
352
|
+
when RubyLLM::Content
|
|
353
|
+
attachments = content.attachments if content.attachments.any?
|
|
354
|
+
content_text = content.text
|
|
355
|
+
when Hash, Array
|
|
356
|
+
content_raw = content
|
|
357
|
+
content_text = nil
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
[content_text, attachments, content_raw]
|
|
361
|
+
end
|
|
334
362
|
end
|
|
335
363
|
end
|
|
336
364
|
end
|
|
@@ -11,6 +11,9 @@ module RubyLLM
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def to_llm
|
|
14
|
+
cached = has_attribute?(:cached_tokens) ? self[:cached_tokens] : nil
|
|
15
|
+
cache_creation = has_attribute?(:cache_creation_tokens) ? self[:cache_creation_tokens] : nil
|
|
16
|
+
|
|
14
17
|
RubyLLM::Message.new(
|
|
15
18
|
role: role.to_sym,
|
|
16
19
|
content: extract_content,
|
|
@@ -18,6 +21,8 @@ module RubyLLM
|
|
|
18
21
|
tool_call_id: extract_tool_call_id,
|
|
19
22
|
input_tokens: input_tokens,
|
|
20
23
|
output_tokens: output_tokens,
|
|
24
|
+
cached_tokens: cached,
|
|
25
|
+
cache_creation_tokens: cache_creation,
|
|
21
26
|
model_id: model_association&.model_id
|
|
22
27
|
)
|
|
23
28
|
end
|
|
@@ -42,9 +47,13 @@ module RubyLLM
|
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def extract_content
|
|
45
|
-
return
|
|
50
|
+
return RubyLLM::Content::Raw.new(content_raw) if has_attribute?(:content_raw) && content_raw.present?
|
|
51
|
+
|
|
52
|
+
content_value = self[:content]
|
|
53
|
+
|
|
54
|
+
return content_value unless respond_to?(:attachments) && attachments.attached?
|
|
46
55
|
|
|
47
|
-
RubyLLM::Content.new(
|
|
56
|
+
RubyLLM::Content.new(content_value).tap do |content_obj|
|
|
48
57
|
@_tempfiles = []
|
|
49
58
|
|
|
50
59
|
attachments.each do |attachment|
|
|
@@ -77,7 +77,7 @@ module RubyLLM
|
|
|
77
77
|
delegate :supports?, :supports_vision?, :supports_functions?, :type,
|
|
78
78
|
:input_price_per_million, :output_price_per_million,
|
|
79
79
|
:function_calling?, :structured_output?, :batch?,
|
|
80
|
-
:reasoning?, :citations?, :streaming?,
|
|
80
|
+
:reasoning?, :citations?, :streaming?, :provider_class,
|
|
81
81
|
to: :to_llm
|
|
82
82
|
end
|
|
83
83
|
end
|
data/lib/ruby_llm/aliases.json
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
"openrouter": "anthropic/claude-3.5-haiku",
|
|
9
9
|
"bedrock": "anthropic.claude-3-5-haiku-20241022-v1:0"
|
|
10
10
|
},
|
|
11
|
+
"claude-3-5-haiku-latest": {
|
|
12
|
+
"anthropic": "claude-3-5-haiku-latest"
|
|
13
|
+
},
|
|
11
14
|
"claude-3-5-sonnet": {
|
|
12
15
|
"anthropic": "claude-3-5-sonnet-20241022",
|
|
13
16
|
"openrouter": "anthropic/claude-3.5-sonnet",
|
|
@@ -18,6 +21,9 @@
|
|
|
18
21
|
"openrouter": "anthropic/claude-3.7-sonnet",
|
|
19
22
|
"bedrock": "us.anthropic.claude-3-7-sonnet-20250219-v1:0"
|
|
20
23
|
},
|
|
24
|
+
"claude-3-7-sonnet-latest": {
|
|
25
|
+
"anthropic": "claude-3-7-sonnet-latest"
|
|
26
|
+
},
|
|
21
27
|
"claude-3-haiku": {
|
|
22
28
|
"anthropic": "claude-3-haiku-20240307",
|
|
23
29
|
"openrouter": "anthropic/claude-3-haiku",
|
|
@@ -31,11 +37,19 @@
|
|
|
31
37
|
"claude-3-sonnet": {
|
|
32
38
|
"bedrock": "anthropic.claude-3-sonnet-20240229-v1:0"
|
|
33
39
|
},
|
|
40
|
+
"claude-haiku-4-5": {
|
|
41
|
+
"anthropic": "claude-haiku-4-5-20251001",
|
|
42
|
+
"openrouter": "anthropic/claude-haiku-4.5",
|
|
43
|
+
"bedrock": "us.anthropic.claude-haiku-4-5-20251001-v1:0"
|
|
44
|
+
},
|
|
34
45
|
"claude-opus-4": {
|
|
35
46
|
"anthropic": "claude-opus-4-20250514",
|
|
36
47
|
"openrouter": "anthropic/claude-opus-4",
|
|
37
48
|
"bedrock": "us.anthropic.claude-opus-4-1-20250805-v1:0"
|
|
38
49
|
},
|
|
50
|
+
"claude-opus-4-0": {
|
|
51
|
+
"anthropic": "claude-opus-4-0"
|
|
52
|
+
},
|
|
39
53
|
"claude-opus-4-1": {
|
|
40
54
|
"anthropic": "claude-opus-4-1-20250805",
|
|
41
55
|
"openrouter": "anthropic/claude-opus-4.1",
|
|
@@ -46,30 +60,18 @@
|
|
|
46
60
|
"openrouter": "anthropic/claude-sonnet-4",
|
|
47
61
|
"bedrock": "us.anthropic.claude-sonnet-4-20250514-v1:0"
|
|
48
62
|
},
|
|
63
|
+
"claude-sonnet-4-0": {
|
|
64
|
+
"anthropic": "claude-sonnet-4-0"
|
|
65
|
+
},
|
|
66
|
+
"claude-sonnet-4-5": {
|
|
67
|
+
"anthropic": "claude-sonnet-4-5-20250929",
|
|
68
|
+
"openrouter": "anthropic/claude-sonnet-4.5",
|
|
69
|
+
"bedrock": "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
|
70
|
+
},
|
|
49
71
|
"deepseek-chat": {
|
|
50
72
|
"deepseek": "deepseek-chat",
|
|
51
73
|
"openrouter": "deepseek/deepseek-chat"
|
|
52
74
|
},
|
|
53
|
-
"gemini-1.5-flash": {
|
|
54
|
-
"gemini": "gemini-1.5-flash",
|
|
55
|
-
"vertexai": "gemini-1.5-flash"
|
|
56
|
-
},
|
|
57
|
-
"gemini-1.5-flash-002": {
|
|
58
|
-
"gemini": "gemini-1.5-flash-002",
|
|
59
|
-
"vertexai": "gemini-1.5-flash-002"
|
|
60
|
-
},
|
|
61
|
-
"gemini-1.5-flash-8b": {
|
|
62
|
-
"gemini": "gemini-1.5-flash-8b",
|
|
63
|
-
"vertexai": "gemini-1.5-flash-8b"
|
|
64
|
-
},
|
|
65
|
-
"gemini-1.5-pro": {
|
|
66
|
-
"gemini": "gemini-1.5-pro",
|
|
67
|
-
"vertexai": "gemini-1.5-pro"
|
|
68
|
-
},
|
|
69
|
-
"gemini-1.5-pro-002": {
|
|
70
|
-
"gemini": "gemini-1.5-pro-002",
|
|
71
|
-
"vertexai": "gemini-1.5-pro-002"
|
|
72
|
-
},
|
|
73
75
|
"gemini-2.0-flash": {
|
|
74
76
|
"gemini": "gemini-2.0-flash",
|
|
75
77
|
"vertexai": "gemini-2.0-flash"
|
|
@@ -93,6 +95,10 @@
|
|
|
93
95
|
"openrouter": "google/gemini-2.5-flash",
|
|
94
96
|
"vertexai": "gemini-2.5-flash"
|
|
95
97
|
},
|
|
98
|
+
"gemini-2.5-flash-image": {
|
|
99
|
+
"gemini": "gemini-2.5-flash-image",
|
|
100
|
+
"openrouter": "google/gemini-2.5-flash-image"
|
|
101
|
+
},
|
|
96
102
|
"gemini-2.5-flash-image-preview": {
|
|
97
103
|
"gemini": "gemini-2.5-flash-image-preview",
|
|
98
104
|
"openrouter": "google/gemini-2.5-flash-image-preview"
|
|
@@ -106,6 +112,14 @@
|
|
|
106
112
|
"gemini": "gemini-2.5-flash-lite-preview-06-17",
|
|
107
113
|
"openrouter": "google/gemini-2.5-flash-lite-preview-06-17"
|
|
108
114
|
},
|
|
115
|
+
"gemini-2.5-flash-lite-preview-09-2025": {
|
|
116
|
+
"gemini": "gemini-2.5-flash-lite-preview-09-2025",
|
|
117
|
+
"openrouter": "google/gemini-2.5-flash-lite-preview-09-2025"
|
|
118
|
+
},
|
|
119
|
+
"gemini-2.5-flash-preview-09-2025": {
|
|
120
|
+
"gemini": "gemini-2.5-flash-preview-09-2025",
|
|
121
|
+
"openrouter": "google/gemini-2.5-flash-preview-09-2025"
|
|
122
|
+
},
|
|
109
123
|
"gemini-2.5-pro": {
|
|
110
124
|
"gemini": "gemini-2.5-pro",
|
|
111
125
|
"openrouter": "google/gemini-2.5-pro",
|
|
@@ -219,6 +233,10 @@
|
|
|
219
233
|
"openai": "gpt-5",
|
|
220
234
|
"openrouter": "openai/gpt-5"
|
|
221
235
|
},
|
|
236
|
+
"gpt-5-codex": {
|
|
237
|
+
"openai": "gpt-5-codex",
|
|
238
|
+
"openrouter": "openai/gpt-5-codex"
|
|
239
|
+
},
|
|
222
240
|
"gpt-5-mini": {
|
|
223
241
|
"openai": "gpt-5-mini",
|
|
224
242
|
"openrouter": "openai/gpt-5-mini"
|
|
@@ -227,6 +245,22 @@
|
|
|
227
245
|
"openai": "gpt-5-nano",
|
|
228
246
|
"openrouter": "openai/gpt-5-nano"
|
|
229
247
|
},
|
|
248
|
+
"gpt-5-pro": {
|
|
249
|
+
"openai": "gpt-5-pro",
|
|
250
|
+
"openrouter": "openai/gpt-5-pro"
|
|
251
|
+
},
|
|
252
|
+
"gpt-oss-120b": {
|
|
253
|
+
"openai": "gpt-oss-120b",
|
|
254
|
+
"openrouter": "openai/gpt-oss-120b"
|
|
255
|
+
},
|
|
256
|
+
"gpt-oss-20b": {
|
|
257
|
+
"openai": "gpt-oss-20b",
|
|
258
|
+
"openrouter": "openai/gpt-oss-20b"
|
|
259
|
+
},
|
|
260
|
+
"imagen-4.0-generate-001": {
|
|
261
|
+
"gemini": "imagen-4.0-generate-001",
|
|
262
|
+
"vertexai": "imagen-4.0-generate-001"
|
|
263
|
+
},
|
|
230
264
|
"o1": {
|
|
231
265
|
"openai": "o1",
|
|
232
266
|
"openrouter": "openai/o1"
|
|
@@ -247,6 +281,10 @@
|
|
|
247
281
|
"openai": "o3",
|
|
248
282
|
"openrouter": "openai/o3"
|
|
249
283
|
},
|
|
284
|
+
"o3-deep-research": {
|
|
285
|
+
"openai": "o3-deep-research",
|
|
286
|
+
"openrouter": "openai/o3-deep-research"
|
|
287
|
+
},
|
|
250
288
|
"o3-mini": {
|
|
251
289
|
"openai": "o3-mini",
|
|
252
290
|
"openrouter": "openai/o3-mini"
|
|
@@ -259,6 +297,10 @@
|
|
|
259
297
|
"openai": "o4-mini",
|
|
260
298
|
"openrouter": "openai/o4-mini"
|
|
261
299
|
},
|
|
300
|
+
"o4-mini-deep-research": {
|
|
301
|
+
"openai": "o4-mini-deep-research",
|
|
302
|
+
"openrouter": "openai/o4-mini-deep-research"
|
|
303
|
+
},
|
|
262
304
|
"text-embedding-004": {
|
|
263
305
|
"gemini": "text-embedding-004",
|
|
264
306
|
"vertexai": "text-embedding-004"
|
data/lib/ruby_llm/attachment.rb
CHANGED
data/lib/ruby_llm/chat.rb
CHANGED
|
@@ -31,7 +31,7 @@ module RubyLLM
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def ask(message = nil, with: nil, &)
|
|
34
|
-
add_message role: :user, content:
|
|
34
|
+
add_message role: :user, content: build_content(message, with)
|
|
35
35
|
complete(&)
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -193,7 +193,8 @@ module RubyLLM
|
|
|
193
193
|
@on[:tool_call]&.call(tool_call)
|
|
194
194
|
result = execute_tool tool_call
|
|
195
195
|
@on[:tool_result]&.call(result)
|
|
196
|
-
|
|
196
|
+
tool_payload = result.is_a?(Tool::Halt) ? result.content : result
|
|
197
|
+
content = content_like?(tool_payload) ? tool_payload : tool_payload.to_s
|
|
197
198
|
message = add_message role: :tool, content:, tool_call_id: tool_call.id
|
|
198
199
|
@on[:end_message]&.call(message)
|
|
199
200
|
|
|
@@ -208,5 +209,15 @@ module RubyLLM
|
|
|
208
209
|
args = tool_call.arguments
|
|
209
210
|
tool.call(args)
|
|
210
211
|
end
|
|
212
|
+
|
|
213
|
+
def build_content(message, attachments)
|
|
214
|
+
return message if content_like?(message)
|
|
215
|
+
|
|
216
|
+
Content.new(message, attachments)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def content_like?(object)
|
|
220
|
+
object.is_a?(Content) || object.is_a?(Content::Raw)
|
|
221
|
+
end
|
|
211
222
|
end
|
|
212
223
|
end
|
|
@@ -10,6 +10,7 @@ module RubyLLM
|
|
|
10
10
|
:openai_use_system_role,
|
|
11
11
|
:anthropic_api_key,
|
|
12
12
|
:gemini_api_key,
|
|
13
|
+
:gemini_api_base,
|
|
13
14
|
:vertexai_project_id,
|
|
14
15
|
:vertexai_location,
|
|
15
16
|
:deepseek_api_key,
|
|
@@ -31,7 +32,9 @@ module RubyLLM
|
|
|
31
32
|
:default_embedding_model,
|
|
32
33
|
:default_moderation_model,
|
|
33
34
|
:default_image_model,
|
|
35
|
+
:default_transcription_model,
|
|
34
36
|
# Model registry
|
|
37
|
+
:model_registry_file,
|
|
35
38
|
:model_registry_class,
|
|
36
39
|
# Rails integration
|
|
37
40
|
:use_new_acts_as,
|
|
@@ -49,7 +52,7 @@ module RubyLLM
|
|
|
49
52
|
:log_stream_debug
|
|
50
53
|
|
|
51
54
|
def initialize
|
|
52
|
-
@request_timeout =
|
|
55
|
+
@request_timeout = 300
|
|
53
56
|
@max_retries = 3
|
|
54
57
|
@retry_interval = 0.1
|
|
55
58
|
@retry_backoff_factor = 2
|
|
@@ -60,7 +63,9 @@ module RubyLLM
|
|
|
60
63
|
@default_embedding_model = 'text-embedding-3-small'
|
|
61
64
|
@default_moderation_model = 'omni-moderation-latest'
|
|
62
65
|
@default_image_model = 'gpt-image-1'
|
|
66
|
+
@default_transcription_model = 'whisper-1'
|
|
63
67
|
|
|
68
|
+
@model_registry_file = File.expand_path('models.json', __dir__)
|
|
64
69
|
@model_registry_class = 'Model'
|
|
65
70
|
@use_new_acts_as = false
|
|
66
71
|
|
data/lib/ruby_llm/connection.rb
CHANGED
|
@@ -34,8 +34,7 @@ module RubyLLM
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def post(url, payload, &)
|
|
37
|
-
|
|
38
|
-
@connection.post url, body do |req|
|
|
37
|
+
@connection.post url, payload do |req|
|
|
39
38
|
req.headers.merge! @provider.headers if @provider.respond_to?(:headers)
|
|
40
39
|
yield req if block_given?
|
|
41
40
|
end
|
|
@@ -77,7 +76,7 @@ module RubyLLM
|
|
|
77
76
|
errors: true,
|
|
78
77
|
headers: false,
|
|
79
78
|
log_level: :debug do |logger|
|
|
80
|
-
logger.filter(%r{[A-Za-z0-9+/=]{100,}}, '
|
|
79
|
+
logger.filter(%r{[A-Za-z0-9+/=]{100,}}, '[BASE64 DATA]')
|
|
81
80
|
logger.filter(/[-\d.e,\s]{100,}/, '[EMBEDDINGS ARRAY]')
|
|
82
81
|
end
|
|
83
82
|
end
|
|
@@ -94,6 +93,7 @@ module RubyLLM
|
|
|
94
93
|
end
|
|
95
94
|
|
|
96
95
|
def setup_middleware(faraday)
|
|
96
|
+
faraday.request :multipart
|
|
97
97
|
faraday.request :json
|
|
98
98
|
faraday.response :json
|
|
99
99
|
faraday.adapter :net_http
|
data/lib/ruby_llm/content.rb
CHANGED
|
@@ -48,3 +48,26 @@ module RubyLLM
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
|
+
|
|
52
|
+
module RubyLLM
|
|
53
|
+
class Content
|
|
54
|
+
# Represents provider-specific payloads that should bypass RubyLLM formatting.
|
|
55
|
+
class Raw
|
|
56
|
+
attr_reader :value
|
|
57
|
+
|
|
58
|
+
def initialize(value)
|
|
59
|
+
raise ArgumentError, 'Raw content payload cannot be nil' if value.nil?
|
|
60
|
+
|
|
61
|
+
@value = value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def format
|
|
65
|
+
@value
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def to_h
|
|
69
|
+
@value
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/ruby_llm/message.rb
CHANGED
|
@@ -5,18 +5,21 @@ module RubyLLM
|
|
|
5
5
|
class Message
|
|
6
6
|
ROLES = %i[system user assistant tool].freeze
|
|
7
7
|
|
|
8
|
-
attr_reader :role, :tool_calls, :tool_call_id, :input_tokens, :output_tokens,
|
|
8
|
+
attr_reader :role, :model_id, :tool_calls, :tool_call_id, :input_tokens, :output_tokens,
|
|
9
|
+
:cached_tokens, :cache_creation_tokens, :raw, :conversation_id
|
|
9
10
|
attr_writer :content
|
|
10
11
|
|
|
11
12
|
def initialize(options = {})
|
|
12
13
|
@role = options.fetch(:role).to_sym
|
|
13
14
|
@content = normalize_content(options.fetch(:content))
|
|
15
|
+
@model_id = options[:model_id]
|
|
14
16
|
@tool_calls = options[:tool_calls]
|
|
17
|
+
@tool_call_id = options[:tool_call_id]
|
|
18
|
+
@conversation_id = options[:conversation_id]
|
|
15
19
|
@input_tokens = options[:input_tokens]
|
|
16
20
|
@output_tokens = options[:output_tokens]
|
|
17
|
-
@
|
|
18
|
-
@
|
|
19
|
-
@tool_call_id = options[:tool_call_id]
|
|
21
|
+
@cached_tokens = options[:cached_tokens]
|
|
22
|
+
@cache_creation_tokens = options[:cache_creation_tokens]
|
|
20
23
|
@raw = options[:raw]
|
|
21
24
|
|
|
22
25
|
ensure_valid_role
|
|
@@ -46,12 +49,14 @@ module RubyLLM
|
|
|
46
49
|
{
|
|
47
50
|
role: role,
|
|
48
51
|
content: content,
|
|
52
|
+
model_id: model_id,
|
|
49
53
|
tool_calls: tool_calls,
|
|
50
54
|
tool_call_id: tool_call_id,
|
|
55
|
+
conversation_id: conversation_id,
|
|
51
56
|
input_tokens: input_tokens,
|
|
52
57
|
output_tokens: output_tokens,
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
cached_tokens: cached_tokens,
|
|
59
|
+
cache_creation_tokens: cache_creation_tokens
|
|
55
60
|
}.compact
|
|
56
61
|
end
|
|
57
62
|
|
data/lib/ruby_llm/model/info.rb
CHANGED
|
@@ -72,6 +72,10 @@ module RubyLLM
|
|
|
72
72
|
pricing.text_tokens.output
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
def provider_class
|
|
76
|
+
RubyLLM::Provider.resolve provider
|
|
77
|
+
end
|
|
78
|
+
|
|
75
79
|
def type # rubocop:disable Metrics/PerceivedComplexity
|
|
76
80
|
if modalities.output.include?('embeddings') && !modalities.output.include?('text')
|
|
77
81
|
'embedding'
|