ruby_llm 1.11.0 → 1.12.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 +4 -4
- data/README.md +12 -0
- data/lib/ruby_llm/active_record/acts_as.rb +0 -2
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +97 -27
- data/lib/ruby_llm/active_record/chat_methods.rb +73 -19
- data/lib/ruby_llm/agent.rb +326 -0
- data/lib/ruby_llm/aliases.json +47 -29
- data/lib/ruby_llm/chat.rb +27 -3
- data/lib/ruby_llm/configuration.rb +3 -0
- data/lib/ruby_llm/content.rb +6 -0
- data/lib/ruby_llm/models.json +19090 -5190
- data/lib/ruby_llm/models.rb +35 -6
- data/lib/ruby_llm/provider.rb +8 -0
- data/lib/ruby_llm/providers/azure/chat.rb +29 -0
- data/lib/ruby_llm/providers/azure/embeddings.rb +24 -0
- data/lib/ruby_llm/providers/azure/media.rb +45 -0
- data/lib/ruby_llm/providers/azure/models.rb +14 -0
- data/lib/ruby_llm/providers/azure.rb +56 -0
- data/lib/ruby_llm/providers/bedrock/auth.rb +122 -0
- data/lib/ruby_llm/providers/bedrock/chat.rb +296 -64
- data/lib/ruby_llm/providers/bedrock/media.rb +62 -33
- data/lib/ruby_llm/providers/bedrock/models.rb +88 -65
- data/lib/ruby_llm/providers/bedrock/streaming.rb +305 -8
- data/lib/ruby_llm/providers/bedrock.rb +61 -52
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +4 -0
- data/lib/tasks/models.rake +10 -5
- data/lib/tasks/vcr.rake +32 -0
- metadata +17 -17
- data/lib/ruby_llm/providers/bedrock/capabilities.rb +0 -167
- data/lib/ruby_llm/providers/bedrock/signing.rb +0 -831
- data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -51
- data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -128
- data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -67
- data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -85
- data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -78
data/lib/tasks/models.rake
CHANGED
|
@@ -39,16 +39,18 @@ end
|
|
|
39
39
|
|
|
40
40
|
def configure_from_env
|
|
41
41
|
RubyLLM.configure do |config|
|
|
42
|
-
config.openai_api_key = ENV.fetch('OPENAI_API_KEY', nil)
|
|
43
42
|
config.anthropic_api_key = ENV.fetch('ANTHROPIC_API_KEY', nil)
|
|
44
|
-
config.
|
|
43
|
+
config.azure_api_base = ENV.fetch('AZURE_API_BASE', nil)
|
|
44
|
+
config.azure_api_key = ENV.fetch('AZURE_API_KEY', nil)
|
|
45
45
|
config.deepseek_api_key = ENV.fetch('DEEPSEEK_API_KEY', nil)
|
|
46
|
-
config.
|
|
47
|
-
config.openrouter_api_key = ENV.fetch('OPENROUTER_API_KEY', nil)
|
|
48
|
-
config.xai_api_key = ENV.fetch('XAI_API_KEY', nil)
|
|
46
|
+
config.gemini_api_key = ENV.fetch('GEMINI_API_KEY', nil)
|
|
49
47
|
config.mistral_api_key = ENV.fetch('MISTRAL_API_KEY', nil)
|
|
48
|
+
config.openai_api_key = ENV.fetch('OPENAI_API_KEY', nil)
|
|
49
|
+
config.openrouter_api_key = ENV.fetch('OPENROUTER_API_KEY', nil)
|
|
50
|
+
config.perplexity_api_key = ENV.fetch('PERPLEXITY_API_KEY', nil)
|
|
50
51
|
config.vertexai_location = ENV.fetch('GOOGLE_CLOUD_LOCATION', nil)
|
|
51
52
|
config.vertexai_project_id = ENV.fetch('GOOGLE_CLOUD_PROJECT', nil)
|
|
53
|
+
config.xai_api_key = ENV.fetch('XAI_API_KEY', nil)
|
|
52
54
|
configure_bedrock(config)
|
|
53
55
|
config.request_timeout = 30
|
|
54
56
|
end
|
|
@@ -331,6 +333,7 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
|
331
333
|
# OpenAI models
|
|
332
334
|
models['openai'].each do |model|
|
|
333
335
|
openrouter_model = "openai/#{model}"
|
|
336
|
+
azure_model = models['azure'].include?(model) ? model : nil
|
|
334
337
|
next unless models['openrouter'].include?(openrouter_model)
|
|
335
338
|
|
|
336
339
|
alias_key = model.gsub('-latest', '')
|
|
@@ -338,6 +341,7 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
|
338
341
|
'openai' => model,
|
|
339
342
|
'openrouter' => openrouter_model
|
|
340
343
|
}
|
|
344
|
+
aliases[alias_key]['azure'] = azure_model if azure_model
|
|
341
345
|
end
|
|
342
346
|
|
|
343
347
|
anthropic_latest = group_anthropic_models_by_base_name(models['anthropic'])
|
|
@@ -358,6 +362,7 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
|
358
362
|
aliases[base_name] = { 'anthropic' => latest_model }
|
|
359
363
|
aliases[base_name]['openrouter'] = openrouter_model if openrouter_model
|
|
360
364
|
aliases[base_name]['bedrock'] = bedrock_model if bedrock_model
|
|
365
|
+
aliases[base_name]['azure'] = latest_model if models['azure'].include?(latest_model)
|
|
361
366
|
end
|
|
362
367
|
|
|
363
368
|
models['bedrock'].each do |bedrock_model|
|
data/lib/tasks/vcr.rake
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'dotenv/load'
|
|
4
|
+
require 'time'
|
|
4
5
|
|
|
5
6
|
def record_all_cassettes(cassette_dir)
|
|
6
7
|
FileUtils.rm_rf(cassette_dir)
|
|
@@ -73,6 +74,31 @@ def run_tests
|
|
|
73
74
|
system('bundle exec rspec') || abort('Tests failed')
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
def retimestamp_cassettes(cassette_dir)
|
|
78
|
+
timestamp = Time.now.utc.httpdate
|
|
79
|
+
updated_files = 0
|
|
80
|
+
updated_entries = 0
|
|
81
|
+
|
|
82
|
+
Dir.glob("#{cassette_dir}/**/*.yml").each do |file|
|
|
83
|
+
content = File.read(file)
|
|
84
|
+
replacements = 0
|
|
85
|
+
|
|
86
|
+
updated = content.gsub(/^(\s*recorded_at:\s*).+$/) do
|
|
87
|
+
replacements += 1
|
|
88
|
+
"#{Regexp.last_match(1)}#{timestamp}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
next if replacements.zero?
|
|
92
|
+
|
|
93
|
+
File.write(file, updated)
|
|
94
|
+
updated_files += 1
|
|
95
|
+
updated_entries += replacements
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
puts "Updated #{updated_entries} recorded_at entries in #{updated_files} cassette files."
|
|
99
|
+
puts "New recorded_at value: #{timestamp}"
|
|
100
|
+
end
|
|
101
|
+
|
|
76
102
|
namespace :vcr do
|
|
77
103
|
desc 'Record VCR cassettes (rake vcr:record[all] or vcr:record[openai,anthropic])'
|
|
78
104
|
task :record, :providers do |_, args|
|
|
@@ -89,4 +115,10 @@ namespace :vcr do
|
|
|
89
115
|
record_for_providers(providers, cassette_dir)
|
|
90
116
|
end
|
|
91
117
|
end
|
|
118
|
+
|
|
119
|
+
desc 'Update recorded_at timestamps for all cassette entries'
|
|
120
|
+
task :retimestamp do
|
|
121
|
+
cassette_dir = 'spec/fixtures/vcr_cassettes'
|
|
122
|
+
retimestamp_cassettes(cassette_dir)
|
|
123
|
+
end
|
|
92
124
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.12.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carmine Paolino
|
|
@@ -43,28 +43,28 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: '2.0'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: '2.0'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: faraday-retry
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '2.0'
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
67
|
+
version: '2.0'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: faraday-multipart
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -99,28 +99,28 @@ dependencies:
|
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '1
|
|
102
|
+
version: '1'
|
|
103
103
|
type: :runtime
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - "~>"
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '1
|
|
109
|
+
version: '1'
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: ruby_llm-schema
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
|
114
114
|
- - "~>"
|
|
115
115
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: 0
|
|
116
|
+
version: '0'
|
|
117
117
|
type: :runtime
|
|
118
118
|
prerelease: false
|
|
119
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
120
|
requirements:
|
|
121
121
|
- - "~>"
|
|
122
122
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: 0
|
|
123
|
+
version: '0'
|
|
124
124
|
- !ruby/object:Gem::Dependency
|
|
125
125
|
name: zeitwerk
|
|
126
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -192,6 +192,7 @@ files:
|
|
|
192
192
|
- lib/ruby_llm/active_record/chat_methods.rb
|
|
193
193
|
- lib/ruby_llm/active_record/message_methods.rb
|
|
194
194
|
- lib/ruby_llm/active_record/model_methods.rb
|
|
195
|
+
- lib/ruby_llm/agent.rb
|
|
195
196
|
- lib/ruby_llm/aliases.json
|
|
196
197
|
- lib/ruby_llm/aliases.rb
|
|
197
198
|
- lib/ruby_llm/attachment.rb
|
|
@@ -226,18 +227,17 @@ files:
|
|
|
226
227
|
- lib/ruby_llm/providers/anthropic/models.rb
|
|
227
228
|
- lib/ruby_llm/providers/anthropic/streaming.rb
|
|
228
229
|
- lib/ruby_llm/providers/anthropic/tools.rb
|
|
230
|
+
- lib/ruby_llm/providers/azure.rb
|
|
231
|
+
- lib/ruby_llm/providers/azure/chat.rb
|
|
232
|
+
- lib/ruby_llm/providers/azure/embeddings.rb
|
|
233
|
+
- lib/ruby_llm/providers/azure/media.rb
|
|
234
|
+
- lib/ruby_llm/providers/azure/models.rb
|
|
229
235
|
- lib/ruby_llm/providers/bedrock.rb
|
|
230
|
-
- lib/ruby_llm/providers/bedrock/
|
|
236
|
+
- lib/ruby_llm/providers/bedrock/auth.rb
|
|
231
237
|
- lib/ruby_llm/providers/bedrock/chat.rb
|
|
232
238
|
- lib/ruby_llm/providers/bedrock/media.rb
|
|
233
239
|
- lib/ruby_llm/providers/bedrock/models.rb
|
|
234
|
-
- lib/ruby_llm/providers/bedrock/signing.rb
|
|
235
240
|
- lib/ruby_llm/providers/bedrock/streaming.rb
|
|
236
|
-
- lib/ruby_llm/providers/bedrock/streaming/base.rb
|
|
237
|
-
- lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb
|
|
238
|
-
- lib/ruby_llm/providers/bedrock/streaming/message_processing.rb
|
|
239
|
-
- lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb
|
|
240
|
-
- lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb
|
|
241
241
|
- lib/ruby_llm/providers/deepseek.rb
|
|
242
242
|
- lib/ruby_llm/providers/deepseek/capabilities.rb
|
|
243
243
|
- lib/ruby_llm/providers/deepseek/chat.rb
|
|
@@ -335,7 +335,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
335
335
|
- !ruby/object:Gem::Version
|
|
336
336
|
version: '0'
|
|
337
337
|
requirements: []
|
|
338
|
-
rubygems_version:
|
|
338
|
+
rubygems_version: 4.0.3
|
|
339
339
|
specification_version: 4
|
|
340
340
|
summary: One beautiful Ruby API for GPT, Claude, Gemini, and more.
|
|
341
341
|
test_files: []
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Providers
|
|
5
|
-
class Bedrock
|
|
6
|
-
# Determines capabilities and pricing for AWS Bedrock models
|
|
7
|
-
module Capabilities
|
|
8
|
-
module_function
|
|
9
|
-
|
|
10
|
-
def context_window_for(model_id)
|
|
11
|
-
case model_id
|
|
12
|
-
when /anthropic\.claude-2/ then 100_000
|
|
13
|
-
else 200_000
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def max_tokens_for(_model_id)
|
|
18
|
-
4_096
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def input_price_for(model_id)
|
|
22
|
-
PRICES.dig(model_family(model_id), :input) || default_input_price
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def output_price_for(model_id)
|
|
26
|
-
PRICES.dig(model_family(model_id), :output) || default_output_price
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def supports_chat?(model_id)
|
|
30
|
-
model_id.match?(/anthropic\.claude/)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def supports_streaming?(model_id)
|
|
34
|
-
model_id.match?(/anthropic\.claude/)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def supports_images?(model_id)
|
|
38
|
-
model_id.match?(/anthropic\.claude/)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def supports_vision?(model_id)
|
|
42
|
-
model_id.match?(/anthropic\.claude/)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def supports_functions?(model_id)
|
|
46
|
-
model_id.match?(/anthropic\.claude/)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def supports_audio?(_model_id)
|
|
50
|
-
false
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def supports_json_mode?(model_id)
|
|
54
|
-
model_id.match?(/anthropic\.claude/)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def format_display_name(model_id)
|
|
58
|
-
model_id.then { |id| humanize(id) }
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def model_type(_model_id)
|
|
62
|
-
'chat'
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def supports_structured_output?(_model_id)
|
|
66
|
-
false
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Model family patterns for capability lookup
|
|
70
|
-
MODEL_FAMILIES = {
|
|
71
|
-
/anthropic\.claude-3-opus/ => :claude3_opus,
|
|
72
|
-
/anthropic\.claude-3-sonnet/ => :claude3_sonnet,
|
|
73
|
-
/anthropic\.claude-3-5-sonnet/ => :claude3_sonnet,
|
|
74
|
-
/anthropic\.claude-3-7-sonnet/ => :claude3_sonnet,
|
|
75
|
-
/anthropic\.claude-3-haiku/ => :claude3_haiku,
|
|
76
|
-
/anthropic\.claude-3-5-haiku/ => :claude3_5_haiku,
|
|
77
|
-
/anthropic\.claude-v2/ => :claude2,
|
|
78
|
-
/anthropic\.claude-instant/ => :claude_instant
|
|
79
|
-
}.freeze
|
|
80
|
-
|
|
81
|
-
def model_family(model_id)
|
|
82
|
-
MODEL_FAMILIES.find { |pattern, _family| model_id.match?(pattern) }&.last || :other
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Pricing information for Bedrock models (per million tokens)
|
|
86
|
-
PRICES = {
|
|
87
|
-
claude3_opus: { input: 15.0, output: 75.0 },
|
|
88
|
-
claude3_sonnet: { input: 3.0, output: 15.0 },
|
|
89
|
-
claude3_haiku: { input: 0.25, output: 1.25 },
|
|
90
|
-
claude3_5_haiku: { input: 0.8, output: 4.0 },
|
|
91
|
-
claude2: { input: 8.0, output: 24.0 },
|
|
92
|
-
claude_instant: { input: 0.8, output: 2.4 }
|
|
93
|
-
}.freeze
|
|
94
|
-
|
|
95
|
-
def default_input_price
|
|
96
|
-
0.1
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def default_output_price
|
|
100
|
-
0.2
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def humanize(id)
|
|
104
|
-
id.tr('-', ' ')
|
|
105
|
-
.split('.')
|
|
106
|
-
.last
|
|
107
|
-
.split
|
|
108
|
-
.map(&:capitalize)
|
|
109
|
-
.join(' ')
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def modalities_for(model_id)
|
|
113
|
-
modalities = {
|
|
114
|
-
input: ['text'],
|
|
115
|
-
output: ['text']
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if model_id.match?(/anthropic\.claude/) && supports_vision?(model_id)
|
|
119
|
-
modalities[:input] << 'image'
|
|
120
|
-
modalities[:input] << 'pdf'
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
modalities
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def capabilities_for(model_id)
|
|
127
|
-
capabilities = []
|
|
128
|
-
|
|
129
|
-
capabilities << 'streaming' if model_id.match?(/anthropic\.claude/)
|
|
130
|
-
|
|
131
|
-
capabilities << 'function_calling' if supports_functions?(model_id)
|
|
132
|
-
|
|
133
|
-
capabilities << 'reasoning' if model_id.match?(/claude-3-7/)
|
|
134
|
-
|
|
135
|
-
if model_id.match?(/claude-3\.5|claude-3-7/)
|
|
136
|
-
capabilities << 'batch'
|
|
137
|
-
capabilities << 'citations'
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
capabilities
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def pricing_for(model_id)
|
|
144
|
-
family = model_family(model_id)
|
|
145
|
-
prices = PRICES.fetch(family, { input: default_input_price, output: default_output_price })
|
|
146
|
-
|
|
147
|
-
standard_pricing = {
|
|
148
|
-
input_per_million: prices[:input],
|
|
149
|
-
output_per_million: prices[:output]
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
batch_pricing = {
|
|
153
|
-
input_per_million: prices[:input] * 0.5,
|
|
154
|
-
output_per_million: prices[:output] * 0.5
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
text_tokens: {
|
|
159
|
-
standard: standard_pricing,
|
|
160
|
-
batch: batch_pricing
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|