ruby_llm 1.13.2 → 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 +16 -13
- data/lib/generators/ruby_llm/agent/agent_generator.rb +36 -0
- data/lib/generators/ruby_llm/agent/templates/agent.rb.tt +6 -0
- data/lib/generators/ruby_llm/agent/templates/instructions.txt.erb.tt +0 -0
- data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +110 -41
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +14 -15
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +8 -11
- data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt +25 -0
- data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt +31 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt +31 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/new.html.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt +27 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_content.html.erb.tt +1 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt +23 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt +10 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt +2 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt +4 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt +14 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt +21 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt +17 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt +40 -0
- data/lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt +27 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +2 -2
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +19 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +5 -3
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +1 -1
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt +6 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt +2 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +4 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt +9 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -7
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt +8 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt +16 -0
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +11 -12
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +27 -17
- data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +3 -4
- data/lib/generators/ruby_llm/generator_helpers.rb +41 -17
- data/lib/generators/ruby_llm/install/install_generator.rb +21 -18
- data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +3 -4
- data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +1 -1
- data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +3 -3
- data/lib/generators/ruby_llm/schema/schema_generator.rb +26 -0
- data/lib/generators/ruby_llm/schema/templates/schema.rb.tt +2 -0
- data/lib/generators/ruby_llm/tool/templates/tool.rb.tt +9 -0
- data/lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt +13 -0
- data/lib/generators/ruby_llm/tool/tool_generator.rb +96 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb +1 -1
- data/lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt +7 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb +49 -0
- data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +5 -7
- data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +1 -1
- data/lib/ruby_llm/active_record/acts_as.rb +6 -26
- data/lib/ruby_llm/active_record/acts_as_legacy.rb +125 -29
- data/lib/ruby_llm/active_record/chat_methods.rb +50 -26
- data/lib/ruby_llm/active_record/message_methods.rb +104 -4
- data/lib/ruby_llm/active_record/model_methods.rb +8 -10
- data/lib/ruby_llm/active_record/payload_helpers.rb +29 -0
- data/lib/ruby_llm/active_record/tool_call_methods.rb +18 -0
- data/lib/ruby_llm/agent.rb +15 -2
- data/lib/ruby_llm/aliases.json +141 -89
- data/lib/ruby_llm/aliases.rb +3 -0
- data/lib/ruby_llm/attachment.rb +44 -40
- data/lib/ruby_llm/chat.rb +229 -59
- data/lib/ruby_llm/configuration.rb +69 -75
- data/lib/ruby_llm/connection.rb +37 -10
- 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 +16 -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 +67 -14
- 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 +21796 -20601
- data/lib/ruby_llm/models.rb +107 -34
- data/lib/ruby_llm/models_schema.json +3 -0
- data/lib/ruby_llm/provider.rb +25 -4
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -133
- data/lib/ruby_llm/providers/anthropic/chat.rb +49 -15
- data/lib/ruby_llm/providers/anthropic/models.rb +4 -8
- 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/anthropic.rb +4 -0
- data/lib/ruby_llm/providers/azure/media.rb +1 -1
- data/lib/ruby_llm/providers/azure.rb +4 -0
- 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 +5 -1
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +28 -103
- data/lib/ruby_llm/providers/deepseek/chat.rb +9 -0
- data/lib/ruby_llm/providers/deepseek.rb +4 -0
- data/lib/ruby_llm/providers/gemini/capabilities.rb +45 -215
- 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/models.rb +2 -4
- 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/gemini.rb +4 -0
- 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/gpustack.rb +4 -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 +5 -1
- data/lib/ruby_llm/providers/ollama/chat.rb +8 -1
- data/lib/ruby_llm/providers/ollama.rb +4 -0
- data/lib/ruby_llm/providers/openai/capabilities.rb +157 -195
- 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/models.rb +2 -4
- 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/openai.rb +10 -0
- data/lib/ruby_llm/providers/openrouter/chat.rb +36 -8
- data/lib/ruby_llm/providers/openrouter/images.rb +3 -3
- 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/openrouter.rb +4 -0
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +34 -99
- 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/models.rb +12 -14
- data/lib/ruby_llm/providers/perplexity.rb +5 -1
- data/lib/ruby_llm/providers/vertexai.rb +8 -0
- 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 +5 -1
- 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 +51 -24
- data/lib/tasks/ruby_llm.rake +6 -5
- data/lib/tasks/vcr.rake +1 -1
- metadata +81 -21
- data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +0 -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,20 +56,17 @@ 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!'
|
|
31
71
|
else
|
|
32
72
|
puts '✅ No stale cassettes found'
|
|
@@ -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"
|
data/lib/tasks/ruby_llm.rake
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
def
|
|
3
|
+
def run_test_queue_rspec
|
|
4
4
|
workers = ENV.fetch('RSPEC_WORKERS', nil)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
env = {}
|
|
6
|
+
env['TEST_QUEUE_WORKERS'] = workers if workers && !workers.empty? && ENV.fetch('TEST_QUEUE_WORKERS', '').empty?
|
|
7
|
+
|
|
8
|
+
system(env, 'bundle', 'exec', 'bin/rspec-queue')
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
namespace :ruby_llm do
|
|
@@ -16,7 +17,7 @@ namespace :ruby_llm do
|
|
|
16
17
|
model_class.save_to_database
|
|
17
18
|
puts "✅ Loaded #{model_class.count} models into database"
|
|
18
19
|
else
|
|
19
|
-
puts 'Model registry not configured. Run rails generate ruby_llm:install'
|
|
20
|
+
puts 'Model registry not configured. Run bin/rails generate ruby_llm:install'
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
end
|
data/lib/tasks/vcr.rake
CHANGED
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
|
|
@@ -43,30 +43,30 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: 1.10.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: 1.10.0
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: faraday-
|
|
55
|
+
name: faraday-multipart
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - ">="
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '1'
|
|
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: '1'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: faraday-
|
|
69
|
+
name: faraday-net_http
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
72
|
- - ">="
|
|
@@ -80,7 +80,7 @@ dependencies:
|
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '1'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name: faraday-
|
|
83
|
+
name: faraday-retry
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - ">="
|
|
@@ -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: []
|
|
@@ -150,21 +150,49 @@ extra_rdoc_files: []
|
|
|
150
150
|
files:
|
|
151
151
|
- LICENSE
|
|
152
152
|
- README.md
|
|
153
|
+
- lib/generators/ruby_llm/agent/agent_generator.rb
|
|
154
|
+
- lib/generators/ruby_llm/agent/templates/agent.rb.tt
|
|
155
|
+
- lib/generators/ruby_llm/agent/templates/instructions.txt.erb.tt
|
|
153
156
|
- lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb
|
|
154
157
|
- lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt
|
|
155
158
|
- lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt
|
|
156
159
|
- lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt
|
|
160
|
+
- lib/generators/ruby_llm/chat_ui/templates/helpers/messages_helper.rb.tt
|
|
157
161
|
- lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt
|
|
162
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_chat.html.erb.tt
|
|
163
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/_form.html.erb.tt
|
|
164
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/index.html.erb.tt
|
|
165
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/new.html.erb.tt
|
|
166
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/chats/show.html.erb.tt
|
|
167
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_assistant.html.erb.tt
|
|
168
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_content.html.erb.tt
|
|
169
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_error.html.erb.tt
|
|
170
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_form.html.erb.tt
|
|
171
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_system.html.erb.tt
|
|
172
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool.html.erb.tt
|
|
173
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_tool_calls.html.erb.tt
|
|
174
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/_user.html.erb.tt
|
|
175
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_calls/_default.html.erb.tt
|
|
176
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/messages/tool_results/_default.html.erb.tt
|
|
177
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/_model.html.erb.tt
|
|
178
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/index.html.erb.tt
|
|
179
|
+
- lib/generators/ruby_llm/chat_ui/templates/tailwind/views/models/show.html.erb.tt
|
|
158
180
|
- lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt
|
|
159
181
|
- lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt
|
|
160
182
|
- lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt
|
|
161
183
|
- lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt
|
|
162
184
|
- lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt
|
|
185
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_assistant.html.erb.tt
|
|
163
186
|
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt
|
|
187
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_error.html.erb.tt
|
|
164
188
|
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt
|
|
165
|
-
- lib/generators/ruby_llm/chat_ui/templates/views/messages/
|
|
189
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_system.html.erb.tt
|
|
190
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool.html.erb.tt
|
|
166
191
|
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt
|
|
192
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/_user.html.erb.tt
|
|
167
193
|
- lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt
|
|
194
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_calls/_default.html.erb.tt
|
|
195
|
+
- lib/generators/ruby_llm/chat_ui/templates/views/messages/tool_results/_default.html.erb.tt
|
|
168
196
|
- lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt
|
|
169
197
|
- lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt
|
|
170
198
|
- lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt
|
|
@@ -180,8 +208,16 @@ files:
|
|
|
180
208
|
- lib/generators/ruby_llm/install/templates/message_model.rb.tt
|
|
181
209
|
- lib/generators/ruby_llm/install/templates/model_model.rb.tt
|
|
182
210
|
- lib/generators/ruby_llm/install/templates/tool_call_model.rb.tt
|
|
211
|
+
- lib/generators/ruby_llm/schema/schema_generator.rb
|
|
212
|
+
- lib/generators/ruby_llm/schema/templates/schema.rb.tt
|
|
213
|
+
- lib/generators/ruby_llm/tool/templates/tool.rb.tt
|
|
214
|
+
- lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt
|
|
215
|
+
- lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt
|
|
216
|
+
- lib/generators/ruby_llm/tool/tool_generator.rb
|
|
183
217
|
- lib/generators/ruby_llm/upgrade_to_v1_10/templates/add_v1_10_message_columns.rb.tt
|
|
184
218
|
- lib/generators/ruby_llm/upgrade_to_v1_10/upgrade_to_v1_10_generator.rb
|
|
219
|
+
- lib/generators/ruby_llm/upgrade_to_v1_14/templates/add_v1_14_tool_call_columns.rb.tt
|
|
220
|
+
- lib/generators/ruby_llm/upgrade_to_v1_14/upgrade_to_v1_14_generator.rb
|
|
185
221
|
- lib/generators/ruby_llm/upgrade_to_v1_7/templates/migration.rb.tt
|
|
186
222
|
- lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb
|
|
187
223
|
- lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt
|
|
@@ -192,6 +228,8 @@ files:
|
|
|
192
228
|
- lib/ruby_llm/active_record/chat_methods.rb
|
|
193
229
|
- lib/ruby_llm/active_record/message_methods.rb
|
|
194
230
|
- lib/ruby_llm/active_record/model_methods.rb
|
|
231
|
+
- lib/ruby_llm/active_record/payload_helpers.rb
|
|
232
|
+
- lib/ruby_llm/active_record/tool_call_methods.rb
|
|
195
233
|
- lib/ruby_llm/agent.rb
|
|
196
234
|
- lib/ruby_llm/aliases.json
|
|
197
235
|
- lib/ruby_llm/aliases.rb
|
|
@@ -202,9 +240,13 @@ files:
|
|
|
202
240
|
- lib/ruby_llm/connection.rb
|
|
203
241
|
- lib/ruby_llm/content.rb
|
|
204
242
|
- lib/ruby_llm/context.rb
|
|
243
|
+
- lib/ruby_llm/cost.rb
|
|
244
|
+
- lib/ruby_llm/deprecator.rb
|
|
205
245
|
- lib/ruby_llm/embedding.rb
|
|
206
246
|
- lib/ruby_llm/error.rb
|
|
247
|
+
- lib/ruby_llm/error_middleware.rb
|
|
207
248
|
- lib/ruby_llm/image.rb
|
|
249
|
+
- lib/ruby_llm/instrumentation.rb
|
|
208
250
|
- lib/ruby_llm/message.rb
|
|
209
251
|
- lib/ruby_llm/mime_type.rb
|
|
210
252
|
- lib/ruby_llm/model.rb
|
|
@@ -213,6 +255,7 @@ files:
|
|
|
213
255
|
- lib/ruby_llm/model/pricing.rb
|
|
214
256
|
- lib/ruby_llm/model/pricing_category.rb
|
|
215
257
|
- lib/ruby_llm/model/pricing_tier.rb
|
|
258
|
+
- lib/ruby_llm/model_registry.rb
|
|
216
259
|
- lib/ruby_llm/models.json
|
|
217
260
|
- lib/ruby_llm/models.rb
|
|
218
261
|
- lib/ruby_llm/models_schema.json
|
|
@@ -260,6 +303,7 @@ files:
|
|
|
260
303
|
- lib/ruby_llm/providers/mistral/capabilities.rb
|
|
261
304
|
- lib/ruby_llm/providers/mistral/chat.rb
|
|
262
305
|
- lib/ruby_llm/providers/mistral/embeddings.rb
|
|
306
|
+
- lib/ruby_llm/providers/mistral/media.rb
|
|
263
307
|
- lib/ruby_llm/providers/mistral/models.rb
|
|
264
308
|
- lib/ruby_llm/providers/ollama.rb
|
|
265
309
|
- lib/ruby_llm/providers/ollama/capabilities.rb
|
|
@@ -286,6 +330,7 @@ files:
|
|
|
286
330
|
- lib/ruby_llm/providers/perplexity.rb
|
|
287
331
|
- lib/ruby_llm/providers/perplexity/capabilities.rb
|
|
288
332
|
- lib/ruby_llm/providers/perplexity/chat.rb
|
|
333
|
+
- lib/ruby_llm/providers/perplexity/media.rb
|
|
289
334
|
- lib/ruby_llm/providers/perplexity/models.rb
|
|
290
335
|
- lib/ruby_llm/providers/vertexai.rb
|
|
291
336
|
- lib/ruby_llm/providers/vertexai/chat.rb
|
|
@@ -303,6 +348,7 @@ files:
|
|
|
303
348
|
- lib/ruby_llm/tokens.rb
|
|
304
349
|
- lib/ruby_llm/tool.rb
|
|
305
350
|
- lib/ruby_llm/tool_call.rb
|
|
351
|
+
- lib/ruby_llm/tool_concurrency.rb
|
|
306
352
|
- lib/ruby_llm/transcription.rb
|
|
307
353
|
- lib/ruby_llm/utils.rb
|
|
308
354
|
- lib/ruby_llm/version.rb
|
|
@@ -316,14 +362,28 @@ licenses:
|
|
|
316
362
|
metadata:
|
|
317
363
|
homepage_uri: https://rubyllm.com
|
|
318
364
|
source_code_uri: https://github.com/crmne/ruby_llm
|
|
319
|
-
changelog_uri: https://github.com/crmne/ruby_llm/
|
|
365
|
+
changelog_uri: https://github.com/crmne/ruby_llm/releases
|
|
320
366
|
documentation_uri: https://rubyllm.com
|
|
321
367
|
bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
|
|
322
368
|
funding_uri: https://github.com/sponsors/crmne
|
|
323
369
|
rubygems_mfa_required: 'true'
|
|
324
370
|
post_install_message: |
|
|
325
|
-
|
|
326
|
-
|
|
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/
|
|
327
387
|
rdoc_options: []
|
|
328
388
|
require_paths:
|
|
329
389
|
- lib
|
|
@@ -338,7 +398,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
338
398
|
- !ruby/object:Gem::Version
|
|
339
399
|
version: '0'
|
|
340
400
|
requirements: []
|
|
341
|
-
rubygems_version: 4.0.
|
|
401
|
+
rubygems_version: 4.0.10
|
|
342
402
|
specification_version: 4
|
|
343
|
-
summary:
|
|
403
|
+
summary: A single, beautiful Ruby framework for all major AI providers.
|
|
344
404
|
test_files: []
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<div id="<%= message_variable_name %>_<%%= <%= message_model_name.demodulize.underscore %>.id %>" class="<%= message_variable_name %>"
|
|
2
|
-
style="margin-bottom: 20px; padding: 10px; border-left: 3px solid <%%= <%= message_model_name.demodulize.underscore %>.role == 'user' ? '#007bff' : '#28a745' %>">
|
|
3
|
-
<div style="font-weight: bold; margin-bottom: 5px;">
|
|
4
|
-
<%%= <%= message_model_name.demodulize.underscore %>.role&.capitalize %>
|
|
5
|
-
</div>
|
|
6
|
-
<div id="<%= message_variable_name %>_<%%= <%= message_model_name.demodulize.underscore %>.id %>_content" style="white-space: pre-wrap;"><%%= <%= message_model_name.demodulize.underscore %>.content %></div>
|
|
7
|
-
<%% if <%= message_model_name.demodulize.underscore %>.tool_call? %>
|
|
8
|
-
<%%= render "<%= message_model_name.underscore.pluralize %>/tool_calls", <%= message_model_name.demodulize.underscore %>: <%= message_model_name.demodulize.underscore %> %>
|
|
9
|
-
<%% end %>
|
|
10
|
-
<div style="font-size: 0.85em; color: #666; margin-top: 5px;">
|
|
11
|
-
<%%= <%= message_model_name.demodulize.underscore %>.created_at&.strftime("%I:%M %p") %>
|
|
12
|
-
</div>
|
|
13
|
-
</div>
|