ruby_llm 1.2.0 → 1.3.0rc1
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 +80 -133
- data/lib/ruby_llm/active_record/acts_as.rb +212 -33
- data/lib/ruby_llm/aliases.json +48 -6
- data/lib/ruby_llm/attachments/audio.rb +12 -0
- data/lib/ruby_llm/attachments/image.rb +9 -0
- data/lib/ruby_llm/attachments/pdf.rb +9 -0
- data/lib/ruby_llm/attachments.rb +78 -0
- data/lib/ruby_llm/chat.rb +22 -19
- data/lib/ruby_llm/configuration.rb +30 -1
- data/lib/ruby_llm/connection.rb +95 -0
- data/lib/ruby_llm/content.rb +51 -72
- data/lib/ruby_llm/context.rb +30 -0
- data/lib/ruby_llm/embedding.rb +13 -5
- data/lib/ruby_llm/error.rb +1 -1
- data/lib/ruby_llm/image.rb +13 -5
- data/lib/ruby_llm/message.rb +12 -4
- data/lib/ruby_llm/mime_types.rb +713 -0
- data/lib/ruby_llm/model_info.rb +208 -27
- data/lib/ruby_llm/models.json +25766 -2154
- data/lib/ruby_llm/models.rb +95 -14
- data/lib/ruby_llm/provider.rb +48 -90
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +76 -13
- data/lib/ruby_llm/providers/anthropic/chat.rb +7 -14
- data/lib/ruby_llm/providers/anthropic/media.rb +44 -34
- data/lib/ruby_llm/providers/anthropic/models.rb +15 -15
- data/lib/ruby_llm/providers/anthropic/tools.rb +2 -2
- data/lib/ruby_llm/providers/anthropic.rb +3 -3
- data/lib/ruby_llm/providers/bedrock/capabilities.rb +61 -2
- data/lib/ruby_llm/providers/bedrock/chat.rb +30 -73
- data/lib/ruby_llm/providers/bedrock/media.rb +56 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +50 -58
- data/lib/ruby_llm/providers/bedrock/streaming/base.rb +16 -0
- data/lib/ruby_llm/providers/bedrock.rb +14 -25
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +35 -2
- data/lib/ruby_llm/providers/deepseek.rb +3 -3
- data/lib/ruby_llm/providers/gemini/capabilities.rb +84 -3
- data/lib/ruby_llm/providers/gemini/chat.rb +8 -37
- data/lib/ruby_llm/providers/gemini/embeddings.rb +18 -34
- data/lib/ruby_llm/providers/gemini/images.rb +2 -2
- data/lib/ruby_llm/providers/gemini/media.rb +39 -110
- data/lib/ruby_llm/providers/gemini/models.rb +16 -22
- data/lib/ruby_llm/providers/gemini/tools.rb +1 -1
- data/lib/ruby_llm/providers/gemini.rb +3 -3
- data/lib/ruby_llm/providers/ollama/chat.rb +28 -0
- data/lib/ruby_llm/providers/ollama/media.rb +44 -0
- data/lib/ruby_llm/providers/ollama.rb +34 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +78 -3
- data/lib/ruby_llm/providers/openai/chat.rb +6 -4
- data/lib/ruby_llm/providers/openai/embeddings.rb +8 -12
- data/lib/ruby_llm/providers/openai/media.rb +38 -21
- data/lib/ruby_llm/providers/openai/models.rb +16 -17
- data/lib/ruby_llm/providers/openai/tools.rb +9 -5
- data/lib/ruby_llm/providers/openai.rb +7 -5
- data/lib/ruby_llm/providers/openrouter/models.rb +88 -0
- data/lib/ruby_llm/providers/openrouter.rb +31 -0
- data/lib/ruby_llm/stream_accumulator.rb +4 -4
- data/lib/ruby_llm/streaming.rb +3 -3
- data/lib/ruby_llm/utils.rb +22 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +15 -5
- data/lib/tasks/models.rake +69 -33
- data/lib/tasks/models_docs.rake +164 -121
- data/lib/tasks/vcr.rake +4 -2
- metadata +23 -14
- data/lib/tasks/browser_helper.rb +0 -97
- data/lib/tasks/capability_generator.rb +0 -123
- data/lib/tasks/capability_scraper.rb +0 -224
- data/lib/tasks/cli_helper.rb +0 -22
- data/lib/tasks/code_validator.rb +0 -29
- data/lib/tasks/model_updater.rb +0 -66
data/lib/ruby_llm/model_info.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'time'
|
4
|
-
|
5
3
|
module RubyLLM
|
6
4
|
# Information about an AI model's capabilities, pricing, and metadata.
|
7
5
|
# Used by the Models registry to help developers choose the right model
|
@@ -13,44 +11,227 @@ module RubyLLM
|
|
13
11
|
# model.supports_functions? # => true
|
14
12
|
# model.input_price_per_million # => 30.0
|
15
13
|
class ModelInfo
|
16
|
-
attr_reader :id, :created_at, :
|
17
|
-
:
|
18
|
-
:supports_json_mode, :input_price_per_million, :output_price_per_million, :type, :family
|
14
|
+
attr_reader :id, :name, :provider, :family, :created_at, :context_window, :max_output_tokens, :knowledge_cutoff,
|
15
|
+
:modalities, :capabilities, :pricing, :metadata
|
19
16
|
|
20
|
-
def initialize(data)
|
17
|
+
def initialize(data)
|
21
18
|
@id = data[:id]
|
22
|
-
@
|
23
|
-
@display_name = data[:display_name]
|
19
|
+
@name = data[:name]
|
24
20
|
@provider = data[:provider]
|
25
|
-
@context_window = data[:context_window]
|
26
|
-
@max_tokens = data[:max_tokens]
|
27
|
-
@type = data[:type]
|
28
21
|
@family = data[:family]
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@
|
33
|
-
@
|
22
|
+
@created_at = data[:created_at]
|
23
|
+
@context_window = data[:context_window]
|
24
|
+
@max_output_tokens = data[:max_output_tokens]
|
25
|
+
@knowledge_cutoff = data[:knowledge_cutoff]
|
26
|
+
@modalities = Modalities.new(data[:modalities] || {})
|
27
|
+
@capabilities = data[:capabilities] || []
|
28
|
+
@pricing = PricingCollection.new(data[:pricing] || {})
|
34
29
|
@metadata = data[:metadata] || {}
|
35
30
|
end
|
36
31
|
|
37
|
-
|
32
|
+
# Capability methods
|
33
|
+
def supports?(capability)
|
34
|
+
capabilities.include?(capability.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
%w[function_calling structured_output batch reasoning citations streaming].each do |cap|
|
38
|
+
define_method "#{cap}?" do
|
39
|
+
supports?(cap)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Backward compatibility methods
|
44
|
+
def display_name
|
45
|
+
name
|
46
|
+
end
|
47
|
+
|
48
|
+
def max_tokens
|
49
|
+
max_output_tokens
|
50
|
+
end
|
51
|
+
|
52
|
+
def supports_vision?
|
53
|
+
modalities.input.include?('image')
|
54
|
+
end
|
55
|
+
|
56
|
+
def supports_functions?
|
57
|
+
function_calling?
|
58
|
+
end
|
59
|
+
|
60
|
+
def input_price_per_million
|
61
|
+
pricing.text_tokens.input
|
62
|
+
end
|
63
|
+
|
64
|
+
def output_price_per_million
|
65
|
+
pricing.text_tokens.output
|
66
|
+
end
|
67
|
+
|
68
|
+
def type # rubocop:disable Metrics/PerceivedComplexity
|
69
|
+
if modalities.output.include?('embeddings') && !modalities.output.include?('text')
|
70
|
+
'embedding'
|
71
|
+
elsif modalities.output.include?('image') && !modalities.output.include?('text')
|
72
|
+
'image'
|
73
|
+
elsif modalities.output.include?('audio') && !modalities.output.include?('text')
|
74
|
+
'audio'
|
75
|
+
elsif modalities.output.include?('moderation')
|
76
|
+
'moderation'
|
77
|
+
else
|
78
|
+
'chat'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_h
|
38
83
|
{
|
39
84
|
id: id,
|
40
|
-
|
41
|
-
display_name: display_name,
|
85
|
+
name: name,
|
42
86
|
provider: provider,
|
43
|
-
context_window: context_window,
|
44
|
-
max_tokens: max_tokens,
|
45
|
-
type: type,
|
46
87
|
family: family,
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
88
|
+
created_at: created_at,
|
89
|
+
context_window: context_window,
|
90
|
+
max_output_tokens: max_output_tokens,
|
91
|
+
knowledge_cutoff: knowledge_cutoff,
|
92
|
+
modalities: modalities.to_h,
|
93
|
+
capabilities: capabilities,
|
94
|
+
pricing: pricing.to_h,
|
52
95
|
metadata: metadata
|
53
96
|
}
|
54
97
|
end
|
55
98
|
end
|
99
|
+
|
100
|
+
# Holds and manages input and output modalities for a language model
|
101
|
+
class Modalities
|
102
|
+
attr_reader :input, :output
|
103
|
+
|
104
|
+
def initialize(data)
|
105
|
+
@input = Array(data[:input]).map(&:to_s)
|
106
|
+
@output = Array(data[:output]).map(&:to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_h
|
110
|
+
{
|
111
|
+
input: input,
|
112
|
+
output: output
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# A collection that manages and provides access to different categories of pricing information
|
118
|
+
# (text tokens, images, audio tokens, embeddings)
|
119
|
+
class PricingCollection
|
120
|
+
def initialize(data)
|
121
|
+
@data = {}
|
122
|
+
|
123
|
+
# Initialize pricing categories
|
124
|
+
%i[text_tokens images audio_tokens embeddings].each do |category|
|
125
|
+
@data[category] = PricingCategory.new(data[category]) if data[category] && !empty_pricing?(data[category])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def method_missing(method, *args)
|
130
|
+
if respond_to_missing?(method)
|
131
|
+
@data[method.to_sym] || PricingCategory.new
|
132
|
+
else
|
133
|
+
super
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def respond_to_missing?(method, include_private = false)
|
138
|
+
%i[text_tokens images audio_tokens embeddings].include?(method.to_sym) || super
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_h
|
142
|
+
@data.transform_values(&:to_h)
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def empty_pricing?(data)
|
148
|
+
# Check if all pricing values in this category are zero or nil
|
149
|
+
return true unless data
|
150
|
+
|
151
|
+
%i[standard batch].each do |tier|
|
152
|
+
next unless data[tier]
|
153
|
+
|
154
|
+
data[tier].each_value do |value|
|
155
|
+
return false if value && value != 0.0
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Represents pricing tiers for different usage categories (standard and batch), managing access to their respective
|
164
|
+
# rates
|
165
|
+
class PricingCategory
|
166
|
+
attr_reader :standard, :batch
|
167
|
+
|
168
|
+
def initialize(data = {})
|
169
|
+
@standard = PricingTier.new(data[:standard] || {}) unless empty_tier?(data[:standard])
|
170
|
+
@batch = PricingTier.new(data[:batch] || {}) unless empty_tier?(data[:batch])
|
171
|
+
end
|
172
|
+
|
173
|
+
# Shorthand methods that default to standard tier
|
174
|
+
def input
|
175
|
+
standard&.input_per_million
|
176
|
+
end
|
177
|
+
|
178
|
+
def output
|
179
|
+
standard&.output_per_million
|
180
|
+
end
|
181
|
+
|
182
|
+
def cached_input
|
183
|
+
standard&.cached_input_per_million
|
184
|
+
end
|
185
|
+
|
186
|
+
# Get value for a specific tier
|
187
|
+
def [](key)
|
188
|
+
key == :batch ? batch : standard
|
189
|
+
end
|
190
|
+
|
191
|
+
def to_h
|
192
|
+
result = {}
|
193
|
+
result[:standard] = standard.to_h if standard
|
194
|
+
result[:batch] = batch.to_h if batch
|
195
|
+
result
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def empty_tier?(tier_data)
|
201
|
+
return true unless tier_data
|
202
|
+
|
203
|
+
tier_data.values.all? { |v| v.nil? || v == 0.0 }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# A dynamic class for storing non-zero pricing values with flexible attribute access
|
208
|
+
class PricingTier
|
209
|
+
def initialize(data = {})
|
210
|
+
@values = {}
|
211
|
+
|
212
|
+
# Only store non-zero values
|
213
|
+
data.each do |key, value|
|
214
|
+
@values[key.to_sym] = value if value && value != 0.0
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def method_missing(method, *args)
|
219
|
+
if method.to_s.end_with?('=')
|
220
|
+
key = method.to_s.chomp('=').to_sym
|
221
|
+
@values[key] = args.first if args.first && args.first != 0.0
|
222
|
+
elsif @values.key?(method)
|
223
|
+
@values[method]
|
224
|
+
else
|
225
|
+
super
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def respond_to_missing?(method, include_private = false)
|
230
|
+
method.to_s.end_with?('=') || @values.key?(method.to_sym) || super
|
231
|
+
end
|
232
|
+
|
233
|
+
def to_h
|
234
|
+
@values
|
235
|
+
end
|
236
|
+
end
|
56
237
|
end
|