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/tasks/models.rake
CHANGED
|
@@ -329,25 +329,26 @@ end
|
|
|
329
329
|
|
|
330
330
|
def standard_pricing_display(model)
|
|
331
331
|
pricing_data = model.pricing.to_h[:text_tokens]&.dig(:standard) || {}
|
|
332
|
+
parts = [
|
|
333
|
+
pricing_part(pricing_data, :input_per_million, 'In'),
|
|
334
|
+
pricing_part(pricing_data, :output_per_million, 'Out'),
|
|
335
|
+
pricing_part(pricing_data, %i[cache_read_input_per_million cached_input_per_million], 'Cache Read'),
|
|
336
|
+
pricing_part(pricing_data, %i[cache_write_input_per_million cache_creation_input_per_million], 'Cache Write')
|
|
337
|
+
].compact
|
|
332
338
|
|
|
333
|
-
if
|
|
334
|
-
parts = []
|
|
339
|
+
return parts.join(', ') if parts.any?
|
|
335
340
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
parts << "Out: $#{format('%.2f', pricing_data[:output_per_million])}" if pricing_data[:output_per_million]
|
|
339
|
-
|
|
340
|
-
if pricing_data[:cached_input_per_million]
|
|
341
|
-
parts << "Cache: $#{format('%.2f', pricing_data[:cached_input_per_million])}"
|
|
342
|
-
end
|
|
341
|
+
'-'
|
|
342
|
+
end
|
|
343
343
|
|
|
344
|
-
|
|
345
|
-
|
|
344
|
+
def pricing_part(pricing_data, key, label)
|
|
345
|
+
key = Array(key).find { |candidate| pricing_data[candidate] }
|
|
346
|
+
return unless key
|
|
346
347
|
|
|
347
|
-
'
|
|
348
|
+
"#{label}: $#{format('%.2f', pricing_data[key])}"
|
|
348
349
|
end
|
|
349
350
|
|
|
350
|
-
def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
351
|
+
def generate_aliases # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
351
352
|
models = Hash.new { |h, k| h[k] = [] }
|
|
352
353
|
|
|
353
354
|
RubyLLM.models.all.each do |model|
|
|
@@ -466,12 +467,40 @@ def generate_aliases # rubocop:disable Metrics/PerceivedComplexity
|
|
|
466
467
|
}
|
|
467
468
|
end
|
|
468
469
|
|
|
470
|
+
add_xai_aliases(aliases, models['xai'])
|
|
471
|
+
|
|
469
472
|
sorted_aliases = aliases.sort.to_h
|
|
470
473
|
File.write(RubyLLM::Aliases.aliases_file, JSON.pretty_generate(sorted_aliases))
|
|
471
474
|
|
|
472
475
|
puts "Generated #{sorted_aliases.size} aliases"
|
|
473
476
|
end
|
|
474
477
|
|
|
478
|
+
def add_xai_aliases(aliases, xai_models)
|
|
479
|
+
return unless xai_models.include?('grok-4.3')
|
|
480
|
+
|
|
481
|
+
%w[
|
|
482
|
+
grok-latest
|
|
483
|
+
grok-3
|
|
484
|
+
grok-3-latest
|
|
485
|
+
grok-3-mini
|
|
486
|
+
grok-3-mini-latest
|
|
487
|
+
grok-4
|
|
488
|
+
grok-4-latest
|
|
489
|
+
grok-4-fast
|
|
490
|
+
grok-4-fast-reasoning
|
|
491
|
+
grok-4-fast-reasoning-latest
|
|
492
|
+
grok-4-fast-non-reasoning
|
|
493
|
+
grok-4-fast-non-reasoning-latest
|
|
494
|
+
grok-4-1-fast
|
|
495
|
+
grok-4-1-fast-reasoning
|
|
496
|
+
grok-4-1-fast-reasoning-latest
|
|
497
|
+
grok-4-1-fast-non-reasoning
|
|
498
|
+
grok-4-1-fast-non-reasoning-latest
|
|
499
|
+
].each do |alias_key|
|
|
500
|
+
aliases[alias_key] ||= { 'xai' => 'grok-4.3' }
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
475
504
|
def group_anthropic_models_by_base_name(anthropic_models)
|
|
476
505
|
grouped = Hash.new { |h, k| h[k] = [] }
|
|
477
506
|
|
|
@@ -518,12 +547,12 @@ def find_best_bedrock_model(anthropic_model, bedrock_models) # rubocop:disable M
|
|
|
518
547
|
when 'claude-instant-v1', 'claude-instant'
|
|
519
548
|
'claude-instant'
|
|
520
549
|
else
|
|
521
|
-
|
|
550
|
+
anthropic_model
|
|
522
551
|
end
|
|
523
552
|
|
|
524
553
|
matching_models = bedrock_models.select do |bedrock_model|
|
|
525
|
-
model_without_prefix = bedrock_model.sub(/^(?:
|
|
526
|
-
model_without_prefix.
|
|
554
|
+
model_without_prefix = bedrock_model.sub(/^(?:(?:[a-z]{2}|global)\.)?anthropic\./, '')
|
|
555
|
+
model_without_prefix.match?(/\A#{Regexp.escape(base_pattern)}(?:-v\d+|:\d+k|$)/)
|
|
527
556
|
end
|
|
528
557
|
|
|
529
558
|
return nil if matching_models.empty?
|
data/lib/tasks/release.rake
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'time'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
# Shared helpers for release-related Rake tasks.
|
|
7
|
+
module ReleaseTasks
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def cassette_recorded_at_times(cassette)
|
|
11
|
+
data = YAML.safe_load_file(cassette, aliases: true)
|
|
12
|
+
|
|
13
|
+
Array(data['http_interactions']).filter_map do |interaction|
|
|
14
|
+
Time.parse(interaction['recorded_at'])
|
|
15
|
+
rescue ArgumentError, TypeError
|
|
16
|
+
nil
|
|
17
|
+
end
|
|
18
|
+
rescue Psych::Exception => e
|
|
19
|
+
abort "Could not parse VCR cassette #{cassette}: #{e.message}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cassette_recorded_at(cassette)
|
|
23
|
+
recorded_at_times = cassette_recorded_at_times(cassette)
|
|
24
|
+
|
|
25
|
+
abort "No recorded_at timestamps found in VCR cassette #{cassette}" if recorded_at_times.empty?
|
|
26
|
+
|
|
27
|
+
recorded_at_times.min
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def find_stale_cassettes(cassette_dir, max_age_days)
|
|
31
|
+
Dir.glob("#{cassette_dir}/**/*.yml").filter_map do |cassette|
|
|
32
|
+
recorded_at = cassette_recorded_at(cassette)
|
|
33
|
+
age_days = (Time.now - recorded_at) / 86_400
|
|
34
|
+
|
|
35
|
+
next unless age_days > max_age_days
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
path: cassette,
|
|
39
|
+
file: File.basename(cassette),
|
|
40
|
+
age: age_days.round(1)
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
3
46
|
namespace :release do # rubocop:disable Metrics/BlockLength
|
|
4
47
|
desc 'Prepare for release'
|
|
5
48
|
task :prepare do
|
|
@@ -13,18 +56,15 @@ namespace :release do # rubocop:disable Metrics/BlockLength
|
|
|
13
56
|
max_age_days = 1
|
|
14
57
|
cassette_dir = 'spec/fixtures/vcr_cassettes'
|
|
15
58
|
|
|
16
|
-
|
|
17
|
-
Dir.glob("#{cassette_dir}/**/*.yml").each do |cassette|
|
|
18
|
-
age_days = (Time.now - File.mtime(cassette)) / 86_400
|
|
19
|
-
next unless age_days > max_age_days
|
|
59
|
+
stale_cassettes = ReleaseTasks.find_stale_cassettes(cassette_dir, max_age_days)
|
|
20
60
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
61
|
+
stale_cassettes.each do |cassette|
|
|
62
|
+
puts "Removing stale cassette: #{cassette[:file]} (#{cassette[:age]} days old)"
|
|
63
|
+
File.delete(cassette[:path])
|
|
24
64
|
end
|
|
25
65
|
|
|
26
|
-
if
|
|
27
|
-
puts "\nšļø Removed #{
|
|
66
|
+
if stale_cassettes.any?
|
|
67
|
+
puts "\nšļø Removed #{stale_cassettes.size} stale cassettes"
|
|
28
68
|
puts 'š Re-recording cassettes...'
|
|
29
69
|
run_test_queue_rspec || exit(1)
|
|
30
70
|
puts 'ā
Cassettes refreshed!'
|
|
@@ -37,25 +77,12 @@ namespace :release do # rubocop:disable Metrics/BlockLength
|
|
|
37
77
|
task :verify_cassettes do
|
|
38
78
|
max_age_days = 1
|
|
39
79
|
cassette_dir = 'spec/fixtures/vcr_cassettes'
|
|
40
|
-
stale_cassettes =
|
|
41
|
-
|
|
42
|
-
Dir.glob("#{cassette_dir}/**/*.yml").each do |cassette|
|
|
43
|
-
age_days = (Time.now - File.mtime(cassette)) / 86_400
|
|
44
|
-
|
|
45
|
-
next unless age_days > max_age_days
|
|
46
|
-
|
|
47
|
-
stale_cassettes << {
|
|
48
|
-
file: File.basename(cassette),
|
|
49
|
-
age: age_days.round(1)
|
|
50
|
-
}
|
|
51
|
-
end
|
|
80
|
+
stale_cassettes = ReleaseTasks.find_stale_cassettes(cassette_dir, max_age_days)
|
|
52
81
|
|
|
53
82
|
if stale_cassettes.any?
|
|
54
83
|
puts "\nā Found stale cassettes (older than #{max_age_days} days):"
|
|
55
|
-
stale_files = []
|
|
56
84
|
stale_cassettes.each do |c|
|
|
57
85
|
puts " - #{c[:file]} (#{c[:age]} days old)"
|
|
58
|
-
stale_files << File.join(cassette_dir, '**', c[:file])
|
|
59
86
|
end
|
|
60
87
|
|
|
61
88
|
puts "\nRun locally: bundle exec rake release:refresh_stale_cassettes"
|
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.16.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carmine Paolino
|
|
@@ -135,13 +135,13 @@ dependencies:
|
|
|
135
135
|
- - "~>"
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
137
|
version: '2'
|
|
138
|
-
description:
|
|
139
|
-
chatbots, AI agents, RAG applications,
|
|
140
|
-
images, audio, PDFs), image generation, embeddings,
|
|
141
|
-
output, Rails integration, and streaming. Works
|
|
142
|
-
AWS Bedrock, DeepSeek, Mistral, Ollama (local
|
|
143
|
-
and any OpenAI-compatible API. Minimal
|
|
144
|
-
Marcel.
|
|
138
|
+
description: A single, beautiful Ruby framework for all major AI providers. Easily
|
|
139
|
+
build chatbots, AI agents, RAG applications, content generators, and every AI workflow
|
|
140
|
+
you can think of. Features chat (text, images, audio, PDFs), image generation, embeddings,
|
|
141
|
+
tools (function calling), structured output, Rails integration, and streaming. Works
|
|
142
|
+
with OpenAI, Anthropic, Google Gemini, AWS Bedrock, DeepSeek, Mistral, Ollama (local
|
|
143
|
+
models), OpenRouter, Perplexity, GPUStack, and any OpenAI-compatible API. Minimal
|
|
144
|
+
dependencies - just Faraday, Zeitwerk, and Marcel.
|
|
145
145
|
email:
|
|
146
146
|
- carmine@paolino.me
|
|
147
147
|
executables: []
|
|
@@ -240,9 +240,13 @@ files:
|
|
|
240
240
|
- lib/ruby_llm/connection.rb
|
|
241
241
|
- lib/ruby_llm/content.rb
|
|
242
242
|
- lib/ruby_llm/context.rb
|
|
243
|
+
- lib/ruby_llm/cost.rb
|
|
244
|
+
- lib/ruby_llm/deprecator.rb
|
|
243
245
|
- lib/ruby_llm/embedding.rb
|
|
244
246
|
- lib/ruby_llm/error.rb
|
|
247
|
+
- lib/ruby_llm/error_middleware.rb
|
|
245
248
|
- lib/ruby_llm/image.rb
|
|
249
|
+
- lib/ruby_llm/instrumentation.rb
|
|
246
250
|
- lib/ruby_llm/message.rb
|
|
247
251
|
- lib/ruby_llm/mime_type.rb
|
|
248
252
|
- lib/ruby_llm/model.rb
|
|
@@ -251,6 +255,7 @@ files:
|
|
|
251
255
|
- lib/ruby_llm/model/pricing.rb
|
|
252
256
|
- lib/ruby_llm/model/pricing_category.rb
|
|
253
257
|
- lib/ruby_llm/model/pricing_tier.rb
|
|
258
|
+
- lib/ruby_llm/model_registry.rb
|
|
254
259
|
- lib/ruby_llm/models.json
|
|
255
260
|
- lib/ruby_llm/models.rb
|
|
256
261
|
- lib/ruby_llm/models_schema.json
|
|
@@ -298,6 +303,7 @@ files:
|
|
|
298
303
|
- lib/ruby_llm/providers/mistral/capabilities.rb
|
|
299
304
|
- lib/ruby_llm/providers/mistral/chat.rb
|
|
300
305
|
- lib/ruby_llm/providers/mistral/embeddings.rb
|
|
306
|
+
- lib/ruby_llm/providers/mistral/media.rb
|
|
301
307
|
- lib/ruby_llm/providers/mistral/models.rb
|
|
302
308
|
- lib/ruby_llm/providers/ollama.rb
|
|
303
309
|
- lib/ruby_llm/providers/ollama/capabilities.rb
|
|
@@ -324,6 +330,7 @@ files:
|
|
|
324
330
|
- lib/ruby_llm/providers/perplexity.rb
|
|
325
331
|
- lib/ruby_llm/providers/perplexity/capabilities.rb
|
|
326
332
|
- lib/ruby_llm/providers/perplexity/chat.rb
|
|
333
|
+
- lib/ruby_llm/providers/perplexity/media.rb
|
|
327
334
|
- lib/ruby_llm/providers/perplexity/models.rb
|
|
328
335
|
- lib/ruby_llm/providers/vertexai.rb
|
|
329
336
|
- lib/ruby_llm/providers/vertexai/chat.rb
|
|
@@ -341,6 +348,7 @@ files:
|
|
|
341
348
|
- lib/ruby_llm/tokens.rb
|
|
342
349
|
- lib/ruby_llm/tool.rb
|
|
343
350
|
- lib/ruby_llm/tool_call.rb
|
|
351
|
+
- lib/ruby_llm/tool_concurrency.rb
|
|
344
352
|
- lib/ruby_llm/transcription.rb
|
|
345
353
|
- lib/ruby_llm/utils.rb
|
|
346
354
|
- lib/ruby_llm/version.rb
|
|
@@ -354,14 +362,28 @@ licenses:
|
|
|
354
362
|
metadata:
|
|
355
363
|
homepage_uri: https://rubyllm.com
|
|
356
364
|
source_code_uri: https://github.com/crmne/ruby_llm
|
|
357
|
-
changelog_uri: https://github.com/crmne/ruby_llm/
|
|
365
|
+
changelog_uri: https://github.com/crmne/ruby_llm/releases
|
|
358
366
|
documentation_uri: https://rubyllm.com
|
|
359
367
|
bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
|
|
360
368
|
funding_uri: https://github.com/sponsors/crmne
|
|
361
369
|
rubygems_mfa_required: 'true'
|
|
362
370
|
post_install_message: |
|
|
363
|
-
|
|
364
|
-
|
|
371
|
+
RubyLLM 1.15 upgrade note:
|
|
372
|
+
|
|
373
|
+
Token accounting is now normalized across providers. `input_tokens` means
|
|
374
|
+
standard input tokens; prompt cache reads and writes are exposed separately
|
|
375
|
+
as `cache_read_tokens` and `cache_write_tokens`.
|
|
376
|
+
|
|
377
|
+
Need request-side input activity?
|
|
378
|
+
input_tokens + cache_read_tokens + cache_write_tokens
|
|
379
|
+
|
|
380
|
+
New cost helpers:
|
|
381
|
+
response.cost.total
|
|
382
|
+
chat.cost.total
|
|
383
|
+
agent.cost.total
|
|
384
|
+
|
|
385
|
+
Upgrading from RubyLLM < 1.15? Read the full upgrade guide:
|
|
386
|
+
https://rubyllm.com/upgrading/
|
|
365
387
|
rdoc_options: []
|
|
366
388
|
require_paths:
|
|
367
389
|
- lib
|
|
@@ -376,7 +398,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
376
398
|
- !ruby/object:Gem::Version
|
|
377
399
|
version: '0'
|
|
378
400
|
requirements: []
|
|
379
|
-
rubygems_version: 4.0.
|
|
401
|
+
rubygems_version: 4.0.10
|
|
380
402
|
specification_version: 4
|
|
381
|
-
summary:
|
|
403
|
+
summary: A single, beautiful Ruby framework for all major AI providers.
|
|
382
404
|
test_files: []
|