ruby_llm 1.14.1 → 1.16.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 +6 -7
- data/lib/generators/ruby_llm/generator_helpers.rb +8 -0
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +1 -1
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +1 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +3 -3
- data/lib/ruby_llm/active_record/acts_as.rb +4 -26
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +123 -29
- data/lib/ruby_llm/active_record/chat_methods.rb +41 -24
- data/lib/ruby_llm/active_record/message_methods.rb +87 -4
- data/lib/ruby_llm/active_record/model_methods.rb +7 -9
- data/lib/ruby_llm/active_record/payload_helpers.rb +3 -0
- data/lib/ruby_llm/active_record/tool_call_methods.rb +3 -0
- data/lib/ruby_llm/agent.rb +4 -2
- data/lib/ruby_llm/aliases.json +108 -75
- data/lib/ruby_llm/aliases.rb +3 -0
- data/lib/ruby_llm/attachment.rb +41 -40
- data/lib/ruby_llm/chat.rb +229 -59
- data/lib/ruby_llm/configuration.rb +14 -1
- data/lib/ruby_llm/connection.rb +36 -7
- data/lib/ruby_llm/content.rb +15 -1
- data/lib/ruby_llm/cost.rb +224 -0
- data/lib/ruby_llm/deprecator.rb +24 -0
- data/lib/ruby_llm/embedding.rb +31 -1
- data/lib/ruby_llm/error.rb +11 -75
- data/lib/ruby_llm/error_middleware.rb +81 -0
- data/lib/ruby_llm/image.rb +39 -4
- data/lib/ruby_llm/instrumentation.rb +36 -0
- data/lib/ruby_llm/message.rb +20 -0
- data/lib/ruby_llm/mime_type.rb +25 -0
- data/lib/ruby_llm/model/info.rb +53 -2
- data/lib/ruby_llm/model/pricing.rb +19 -9
- data/lib/ruby_llm/model/pricing_category.rb +13 -2
- data/lib/ruby_llm/model/pricing_tier.rb +20 -9
- data/lib/ruby_llm/model_registry.rb +39 -0
- data/lib/ruby_llm/models.json +17817 -13942
- data/lib/ruby_llm/models.rb +97 -31
- data/lib/ruby_llm/models_schema.json +3 -0
- data/lib/ruby_llm/provider.rb +20 -4
- data/lib/ruby_llm/providers/anthropic/chat.rb +49 -15
- data/lib/ruby_llm/providers/anthropic/models.rb +2 -0
- data/lib/ruby_llm/providers/anthropic/streaming.rb +2 -0
- data/lib/ruby_llm/providers/anthropic/tools.rb +32 -3
- data/lib/ruby_llm/providers/azure/media.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/auth.rb +1 -0
- data/lib/ruby_llm/providers/bedrock/chat.rb +26 -13
- data/lib/ruby_llm/providers/bedrock/media.rb +21 -3
- data/lib/ruby_llm/providers/bedrock/models.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming.rb +10 -1
- data/lib/ruby_llm/providers/bedrock.rb +2 -2
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +43 -0
- data/lib/ruby_llm/providers/deepseek/chat.rb +9 -0
- data/lib/ruby_llm/providers/gemini/chat.rb +10 -4
- data/lib/ruby_llm/providers/gemini/images.rb +2 -2
- data/lib/ruby_llm/providers/gemini/media.rb +16 -9
- data/lib/ruby_llm/providers/gemini/streaming.rb +6 -1
- data/lib/ruby_llm/providers/gemini/tools.rb +5 -1
- data/lib/ruby_llm/providers/gpustack/chat.rb +8 -1
- data/lib/ruby_llm/providers/gpustack/models.rb +2 -0
- data/lib/ruby_llm/providers/mistral/capabilities.rb +7 -2
- data/lib/ruby_llm/providers/mistral/chat.rb +56 -5
- data/lib/ruby_llm/providers/mistral/media.rb +55 -0
- data/lib/ruby_llm/providers/mistral/models.rb +2 -0
- data/lib/ruby_llm/providers/mistral.rb +2 -2
- data/lib/ruby_llm/providers/ollama/chat.rb +8 -1
- data/lib/ruby_llm/providers/openai/capabilities.rb +82 -12
- data/lib/ruby_llm/providers/openai/chat.rb +61 -7
- data/lib/ruby_llm/providers/openai/images.rb +58 -6
- data/lib/ruby_llm/providers/openai/media.rb +40 -16
- data/lib/ruby_llm/providers/openai/streaming.rb +7 -6
- data/lib/ruby_llm/providers/openai/tools.rb +2 -0
- data/lib/ruby_llm/providers/openai/transcription.rb +1 -0
- data/lib/ruby_llm/providers/openrouter/chat.rb +36 -8
- data/lib/ruby_llm/providers/openrouter/images.rb +2 -2
- data/lib/ruby_llm/providers/openrouter/models.rb +1 -1
- data/lib/ruby_llm/providers/openrouter/streaming.rb +5 -6
- data/lib/ruby_llm/providers/perplexity/chat.rb +11 -0
- data/lib/ruby_llm/providers/perplexity/media.rb +62 -0
- data/lib/ruby_llm/providers/perplexity.rb +2 -2
- data/lib/ruby_llm/providers/vertexai.rb +5 -1
- data/lib/ruby_llm/providers/xai/chat.rb +9 -0
- data/lib/ruby_llm/providers/xai/models.rb +15 -27
- data/lib/ruby_llm/providers/xai.rb +2 -2
- data/lib/ruby_llm/railtie.rb +11 -1
- data/lib/ruby_llm/stream_accumulator.rb +45 -30
- data/lib/ruby_llm/streaming.rb +4 -0
- data/lib/ruby_llm/tokens.rb +8 -0
- data/lib/ruby_llm/tool.rb +24 -7
- data/lib/ruby_llm/tool_concurrency.rb +105 -0
- data/lib/ruby_llm/transcription.rb +2 -1
- data/lib/ruby_llm/utils.rb +39 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +11 -6
- data/lib/tasks/models.rake +45 -16
- data/lib/tasks/release.rake +50 -23
- metadata +35 -13
data/lib/ruby_llm/model/info.rb
CHANGED
|
@@ -5,7 +5,7 @@ module RubyLLM
|
|
|
5
5
|
# Information about an AI model's capabilities, pricing, and metadata.
|
|
6
6
|
class Info
|
|
7
7
|
attr_reader :id, :name, :provider, :family, :created_at, :context_window, :max_output_tokens, :knowledge_cutoff,
|
|
8
|
-
:modalities, :capabilities, :pricing, :metadata
|
|
8
|
+
:modalities, :capabilities, :pricing, :metadata, :reasoning_options
|
|
9
9
|
|
|
10
10
|
# Create a default model with assumed capabilities
|
|
11
11
|
def self.default(model_id, provider)
|
|
@@ -31,7 +31,9 @@ module RubyLLM
|
|
|
31
31
|
@modalities = Modalities.new(data[:modalities] || {})
|
|
32
32
|
@capabilities = data[:capabilities] || []
|
|
33
33
|
@pricing = Pricing.new(data[:pricing] || {})
|
|
34
|
-
@metadata = data[:metadata] || {}
|
|
34
|
+
@metadata = data[:metadata]&.dup || {}
|
|
35
|
+
@reasoning_options = normalize_reasoning_options(reasoning_options_from(data))
|
|
36
|
+
store_reasoning_options_metadata
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
def supports?(capability)
|
|
@@ -61,6 +63,14 @@ module RubyLLM
|
|
|
61
63
|
modalities.input.include?('image')
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
def reasoning_option(type)
|
|
67
|
+
reasoning_options.find { |option| option[:type] == type.to_s }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def reasoning_option_values(type)
|
|
71
|
+
Array(reasoning_option(type)&.fetch(:values, nil))
|
|
72
|
+
end
|
|
73
|
+
|
|
64
74
|
def supports_video?
|
|
65
75
|
modalities.input.include?('video')
|
|
66
76
|
end
|
|
@@ -77,6 +87,23 @@ module RubyLLM
|
|
|
77
87
|
pricing.text_tokens.output
|
|
78
88
|
end
|
|
79
89
|
|
|
90
|
+
def cache_read_input_price_per_million
|
|
91
|
+
pricing.text_tokens.cache_read_input
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def cache_write_input_price_per_million
|
|
95
|
+
pricing.text_tokens.cache_write_input
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
alias cached_input_price_per_million cache_read_input_price_per_million
|
|
99
|
+
alias cache_creation_input_price_per_million cache_write_input_price_per_million
|
|
100
|
+
|
|
101
|
+
def cost_for(tokens)
|
|
102
|
+
tokens = tokens.tokens if tokens.respond_to?(:tokens)
|
|
103
|
+
|
|
104
|
+
Cost.new(tokens:, model: self)
|
|
105
|
+
end
|
|
106
|
+
|
|
80
107
|
def provider_class
|
|
81
108
|
RubyLLM::Provider.resolve provider
|
|
82
109
|
end
|
|
@@ -108,6 +135,30 @@ module RubyLLM
|
|
|
108
135
|
metadata: metadata
|
|
109
136
|
}
|
|
110
137
|
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def reasoning_options_from(data)
|
|
142
|
+
data[:reasoning_options] || metadata[:reasoning_options] || metadata['reasoning_options']
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def store_reasoning_options_metadata
|
|
146
|
+
return unless reasoning_options.any?
|
|
147
|
+
|
|
148
|
+
metadata.delete('reasoning_options')
|
|
149
|
+
metadata[:reasoning_options] = reasoning_options
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def normalize_reasoning_options(options)
|
|
153
|
+
Array(options).filter_map do |option|
|
|
154
|
+
next unless option.is_a?(Hash)
|
|
155
|
+
|
|
156
|
+
normalized = option.to_h.transform_keys(&:to_sym)
|
|
157
|
+
normalized[:type] = normalized[:type].to_s if normalized[:type]
|
|
158
|
+
normalized[:values] = Array(normalized[:values]).map(&:to_s) if normalized.key?(:values)
|
|
159
|
+
normalized
|
|
160
|
+
end
|
|
161
|
+
end
|
|
111
162
|
end
|
|
112
163
|
end
|
|
113
164
|
end
|
|
@@ -4,24 +4,30 @@ module RubyLLM
|
|
|
4
4
|
module Model
|
|
5
5
|
# A collection that manages and provides access to different categories of pricing information
|
|
6
6
|
class Pricing
|
|
7
|
+
CATEGORIES = %i[text_tokens images audio_tokens embeddings].freeze
|
|
8
|
+
|
|
7
9
|
def initialize(data)
|
|
8
10
|
@data = {}
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
CATEGORIES.each do |category|
|
|
11
13
|
@data[category] = PricingCategory.new(data[category]) if data[category] && !empty_pricing?(data[category])
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
def text_tokens
|
|
18
|
+
category(:text_tokens)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def images
|
|
22
|
+
category(:images)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
+
def audio_tokens
|
|
26
|
+
category(:audio_tokens)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def embeddings
|
|
30
|
+
category(:embeddings)
|
|
25
31
|
end
|
|
26
32
|
|
|
27
33
|
def to_h
|
|
@@ -30,6 +36,10 @@ module RubyLLM
|
|
|
30
36
|
|
|
31
37
|
private
|
|
32
38
|
|
|
39
|
+
def category(name)
|
|
40
|
+
@data[name] || PricingCategory.new
|
|
41
|
+
end
|
|
42
|
+
|
|
33
43
|
def empty_pricing?(data)
|
|
34
44
|
return true unless data
|
|
35
45
|
|
|
@@ -19,10 +19,21 @@ module RubyLLM
|
|
|
19
19
|
standard&.output_per_million
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
standard&.cached_input_per_million
|
|
22
|
+
def cache_read_input
|
|
23
|
+
standard&.cache_read_input_per_million || standard&.cached_input_per_million
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def cache_write_input
|
|
27
|
+
standard&.cache_write_input_per_million || standard&.cache_creation_input_per_million
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def reasoning_output
|
|
31
|
+
standard&.reasoning_output_per_million
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
alias cached_input cache_read_input
|
|
35
|
+
alias cache_creation_input cache_write_input
|
|
36
|
+
|
|
26
37
|
def [](key)
|
|
27
38
|
key == :batch ? batch : standard
|
|
28
39
|
end
|
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module RubyLLM
|
|
4
4
|
module Model
|
|
5
|
-
#
|
|
5
|
+
# Stores non-zero pricing values for a single pricing tier.
|
|
6
6
|
class PricingTier
|
|
7
|
+
ATTRIBUTES = %i[
|
|
8
|
+
input_per_million
|
|
9
|
+
output_per_million
|
|
10
|
+
cache_read_input_per_million
|
|
11
|
+
cache_write_input_per_million
|
|
12
|
+
cached_input_per_million
|
|
13
|
+
cache_creation_input_per_million
|
|
14
|
+
reasoning_output_per_million
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
7
17
|
def initialize(data = {})
|
|
8
18
|
@values = {}
|
|
9
19
|
|
|
@@ -12,17 +22,18 @@ module RubyLLM
|
|
|
12
22
|
end
|
|
13
23
|
end
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
ATTRIBUTES.each do |attribute|
|
|
26
|
+
define_method(attribute) do
|
|
27
|
+
@values[attribute]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
define_method("#{attribute}=") do |value|
|
|
31
|
+
@values[attribute] = value if value && value != 0.0
|
|
21
32
|
end
|
|
22
33
|
end
|
|
23
34
|
|
|
24
|
-
def
|
|
25
|
-
|
|
35
|
+
def [](key)
|
|
36
|
+
@values[key.to_sym]
|
|
26
37
|
end
|
|
27
38
|
|
|
28
39
|
def to_h
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
# Sources for model registry data.
|
|
5
|
+
module ModelRegistry
|
|
6
|
+
# Reads model registry data from the configured JSON file.
|
|
7
|
+
class JsonSource
|
|
8
|
+
def initialize(file = nil)
|
|
9
|
+
@file = file
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def read
|
|
13
|
+
Models.read_from_json(@file || RubyLLM.config.model_registry_file)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Reads model registry data from the configured Active Record model class.
|
|
18
|
+
class ActiveRecordSource
|
|
19
|
+
def read
|
|
20
|
+
model_class = resolve_model_class
|
|
21
|
+
return [] unless model_class.respond_to?(:table_exists?) && model_class.table_exists?
|
|
22
|
+
|
|
23
|
+
model_class.all.map(&:to_llm)
|
|
24
|
+
rescue StandardError => e
|
|
25
|
+
RubyLLM.logger.debug { "Failed to load models from database: #{e.message}, falling back to JSON" }
|
|
26
|
+
[]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def resolve_model_class
|
|
32
|
+
model_class = RubyLLM.config.model_registry_class
|
|
33
|
+
return model_class unless model_class.is_a?(String)
|
|
34
|
+
|
|
35
|
+
model_class.split('::').inject(Object) { |scope, name| scope.const_get(name) }
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|