ruby_llm 2.0.0.rc2 → 2.0.0.rc4
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/.rdoc_options +2 -2
- data/README.md +32 -21
- data/lib/generators/ruby_llm/upgrade/legacy_content_sql.rb +34 -0
- data/lib/generators/ruby_llm/upgrade/online_copy_migration/data.rb +341 -0
- data/lib/generators/ruby_llm/upgrade/online_copy_migration/journal.rb +171 -0
- data/lib/generators/ruby_llm/upgrade/online_copy_migration/verification.rb +197 -0
- data/lib/generators/ruby_llm/upgrade/online_copy_migration.rb +174 -0
- data/lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt +19 -12
- data/lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt +6 -6
- data/lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt +38 -6
- data/lib/generators/ruby_llm/upgrade/templates/prepare_v2_upgrade.rb.tt +16 -14
- data/lib/generators/ruby_llm/upgrade/templates/ruby_llm_upgrade.rb.tt +37 -8
- data/lib/generators/ruby_llm/upgrade/templates/upgrade_initializer.rb.tt +1 -1
- data/lib/generators/ruby_llm/upgrade/upgrade_generator.rb +26 -5
- data/lib/generators/ruby_llm/upgrade/upgrade_migration.rb +16 -1
- data/lib/ruby_llm/accounting/usage.rb +9 -0
- data/lib/ruby_llm/agent.rb +10 -9
- data/lib/ruby_llm/aliases.json +26 -4
- data/lib/ruby_llm/attachment.rb +5 -0
- data/lib/ruby_llm/batch.rb +2 -2
- data/lib/ruby_llm/chat.rb +6 -2
- data/lib/ruby_llm/embedding.rb +1 -1
- data/lib/ruby_llm/image.rb +1 -1
- data/lib/ruby_llm/message.rb +14 -5
- data/lib/ruby_llm/models.json +5364 -3027
- data/lib/ruby_llm/moderation.rb +1 -1
- data/lib/ruby_llm/ocr.rb +1 -1
- data/lib/ruby_llm/protocols/anthropic/chat.rb +1 -6
- data/lib/ruby_llm/protocols/bedrock/async_videos.rb +2 -1
- data/lib/ruby_llm/protocols/chat_completions/chat.rb +2 -11
- data/lib/ruby_llm/protocols/chat_completions/rerank.rb +8 -1
- data/lib/ruby_llm/protocols/cohere/rerank.rb +8 -1
- data/lib/ruby_llm/protocols/converse/chat.rb +0 -1
- data/lib/ruby_llm/protocols/gemini/embedding_batches.rb +5 -0
- data/lib/ruby_llm/protocols/interactions/tools.rb +3 -1
- data/lib/ruby_llm/protocols/responses/chat.rb +0 -10
- data/lib/ruby_llm/providers/deepseek/responses.rb +0 -1
- data/lib/ruby_llm/providers/mistral/ocr.rb +5 -1
- data/lib/ruby_llm/providers/openrouter/chat.rb +1 -5
- data/lib/ruby_llm/rerank.rb +1 -1
- data/lib/ruby_llm/speech.rb +1 -1
- data/lib/ruby_llm/transcription.rb +1 -1
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm/video_job.rb +1 -1
- data/lib/ruby_llm.rb +1 -1
- data/lib/tasks/ruby_llm.rake +1 -1
- metadata +11 -6
data/lib/ruby_llm/moderation.rb
CHANGED
|
@@ -104,7 +104,7 @@ module RubyLLM
|
|
|
104
104
|
empty_tokens = Tokens.new
|
|
105
105
|
payload = {
|
|
106
106
|
provider: provider_instance.slug,
|
|
107
|
-
provider_class: provider_instance.
|
|
107
|
+
provider_class: provider_instance.name,
|
|
108
108
|
model: model&.id,
|
|
109
109
|
model_info: model,
|
|
110
110
|
input: input,
|
data/lib/ruby_llm/ocr.rb
CHANGED
|
@@ -39,13 +39,12 @@ module RubyLLM
|
|
|
39
39
|
warn_unsupported_citations(model) if citations && !model.supports?(:citations)
|
|
40
40
|
tool_prefs ||= {}
|
|
41
41
|
system_messages, chat_messages = separate_messages(messages)
|
|
42
|
-
explicit_boundaries = cache_boundaries?(messages, caching:)
|
|
43
42
|
system_content = build_system_content(system_messages, caching:)
|
|
44
43
|
|
|
45
44
|
build_base_payload(chat_messages, model, stream, thinking, citations: citations, caching:,
|
|
46
45
|
max_output_tokens:).tap do |payload|
|
|
47
46
|
add_optional_fields(payload, system_content:, tools:, tool_prefs:, temperature:, schema:)
|
|
48
|
-
payload[:cache_control] = prompt_cache_control(caching) if caching
|
|
47
|
+
payload[:cache_control] = prompt_cache_control(caching) if caching
|
|
49
48
|
end
|
|
50
49
|
end
|
|
51
50
|
|
|
@@ -429,10 +428,6 @@ module RubyLLM
|
|
|
429
428
|
content_blocks.concat(Media.format_content(msg.content, msg.attachments, citations: citations))
|
|
430
429
|
end
|
|
431
430
|
|
|
432
|
-
def cache_boundaries?(messages, caching: nil)
|
|
433
|
-
caching != false && messages.any?(&:cache_until_here?)
|
|
434
|
-
end
|
|
435
|
-
|
|
436
431
|
def cache_boundary?(message, caching: nil)
|
|
437
432
|
caching != false && message.cache_until_here?
|
|
438
433
|
end
|
|
@@ -64,7 +64,8 @@ module RubyLLM
|
|
|
64
64
|
prefix = job.raw.dig('outputDataConfig', 's3OutputDataConfig', 's3Uri')
|
|
65
65
|
raise Error, 'Bedrock video job has no output S3 URI' unless prefix
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
prefix_length = (prefix.rindex(%r{[^/]}) || -1) + 1
|
|
68
|
+
uris = @provider.list_file_uris("#{prefix[0, prefix_length]}/")
|
|
68
69
|
videos = uris.select { |uri| uri.downcase.end_with?('.mp4') }
|
|
69
70
|
raise Error, 'Expected exactly one MP4 in the Bedrock video output' unless videos.one?
|
|
70
71
|
|
|
@@ -87,7 +87,7 @@ module RubyLLM
|
|
|
87
87
|
payload[:reasoning_effort] = effort if effort
|
|
88
88
|
|
|
89
89
|
payload[:stream_options] = { include_usage: true } if stream
|
|
90
|
-
apply_prompt_cache_params(payload,
|
|
90
|
+
apply_prompt_cache_params(payload, caching)
|
|
91
91
|
payload
|
|
92
92
|
end
|
|
93
93
|
|
|
@@ -250,11 +250,10 @@ module RubyLLM
|
|
|
250
250
|
end
|
|
251
251
|
end
|
|
252
252
|
|
|
253
|
-
def apply_prompt_cache_params(payload,
|
|
253
|
+
def apply_prompt_cache_params(payload, caching)
|
|
254
254
|
return unless openai_prompt_caching?
|
|
255
255
|
|
|
256
256
|
payload.merge!(prompt_cache_params(caching)) if caching
|
|
257
|
-
force_explicit_cache_mode(payload) if caching != false && cache_boundaries?(messages)
|
|
258
257
|
end
|
|
259
258
|
|
|
260
259
|
def openai_prompt_caching?
|
|
@@ -290,14 +289,6 @@ module RubyLLM
|
|
|
290
289
|
retention
|
|
291
290
|
end
|
|
292
291
|
|
|
293
|
-
def force_explicit_cache_mode(payload)
|
|
294
|
-
payload[:prompt_cache_options] = { mode: 'explicit' }.merge(payload[:prompt_cache_options] || {})
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
def cache_boundaries?(messages)
|
|
298
|
-
messages.any?(&:cache_until_here?)
|
|
299
|
-
end
|
|
300
|
-
|
|
301
292
|
def prompt_cache_options(caching)
|
|
302
293
|
options = caching.to_h.transform_keys(&:to_sym)
|
|
303
294
|
unsupported = options.keys - PROMPT_CACHE_OPTIONS
|
|
@@ -38,10 +38,13 @@ module RubyLLM
|
|
|
38
38
|
def parse_rerank_results(data, documents = [])
|
|
39
39
|
Array(data['results']).map do |result|
|
|
40
40
|
index = result['index']
|
|
41
|
+
unless valid_rerank_index?(index, documents)
|
|
42
|
+
raise Error, 'Rerank endpoint returned an invalid document index'
|
|
43
|
+
end
|
|
41
44
|
|
|
42
45
|
RubyLLM::Rerank::Result.new(
|
|
43
46
|
index: index,
|
|
44
|
-
document: rerank_document(result['document']) ||
|
|
47
|
+
document: rerank_document(result['document']) || documents[index],
|
|
45
48
|
score: result['relevance_score']
|
|
46
49
|
)
|
|
47
50
|
end
|
|
@@ -50,6 +53,10 @@ module RubyLLM
|
|
|
50
53
|
def rerank_document(document)
|
|
51
54
|
document.is_a?(Hash) ? document['text'] : document
|
|
52
55
|
end
|
|
56
|
+
|
|
57
|
+
def valid_rerank_index?(index, documents)
|
|
58
|
+
index.is_a?(Integer) && index.between?(0, documents.length - 1)
|
|
59
|
+
end
|
|
53
60
|
end
|
|
54
61
|
end
|
|
55
62
|
end
|
|
@@ -38,14 +38,21 @@ module RubyLLM
|
|
|
38
38
|
def parse_rerank_results(data, documents)
|
|
39
39
|
Array(data['results']).map do |result|
|
|
40
40
|
index = result['index']
|
|
41
|
+
unless valid_rerank_index?(index, documents)
|
|
42
|
+
raise Error, 'Cohere reranking returned an invalid document index'
|
|
43
|
+
end
|
|
41
44
|
|
|
42
45
|
RubyLLM::Rerank::Result.new(
|
|
43
46
|
index: index,
|
|
44
|
-
document: result.dig('document', 'text') ||
|
|
47
|
+
document: result.dig('document', 'text') || documents[index],
|
|
45
48
|
score: result['relevance_score']
|
|
46
49
|
)
|
|
47
50
|
end
|
|
48
51
|
end
|
|
52
|
+
|
|
53
|
+
def valid_rerank_index?(index, documents)
|
|
54
|
+
index.is_a?(Integer) && index.between?(0, documents.length - 1)
|
|
55
|
+
end
|
|
49
56
|
end
|
|
50
57
|
end
|
|
51
58
|
end
|
|
@@ -266,7 +266,6 @@ module RubyLLM
|
|
|
266
266
|
|
|
267
267
|
def automatic_cache_target(system_messages, chat_messages, caching)
|
|
268
268
|
return unless caching
|
|
269
|
-
return if (system_messages + chat_messages).any?(&:cache_until_here?)
|
|
270
269
|
|
|
271
270
|
(chat_messages.reverse + system_messages.reverse).find { |msg| cacheable_message?(msg) }
|
|
272
271
|
end
|
|
@@ -60,6 +60,11 @@ module RubyLLM
|
|
|
60
60
|
metadata = responses.first.fetch('metadata')
|
|
61
61
|
return if responses.size < metadata.fetch('embedding_count')
|
|
62
62
|
|
|
63
|
+
positions = responses.map { |inline| inline.dig('metadata', 'embedding_index') }
|
|
64
|
+
unless positions.sort == (0...responses.size).to_a
|
|
65
|
+
return [index, nil, batch_failure(key, 'Invalid or duplicate embedding record positions')]
|
|
66
|
+
end
|
|
67
|
+
|
|
63
68
|
vectors = embedding_batch_vectors(responses)
|
|
64
69
|
return [index, nil, batch_failure(key, 'Gemini returned no embedding')] unless vectors
|
|
65
70
|
|
|
@@ -31,7 +31,9 @@ module RubyLLM
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def parse_interaction_arguments(arguments)
|
|
34
|
-
arguments.is_a?(String)
|
|
34
|
+
return {} if arguments.nil? || (arguments.is_a?(String) && arguments.empty?)
|
|
35
|
+
|
|
36
|
+
arguments.is_a?(String) ? JSON.parse(arguments) : arguments
|
|
35
37
|
rescue JSON::ParserError => e
|
|
36
38
|
raise ToolCallParseError.new(finish_reason: :tool_calls), cause: e
|
|
37
39
|
end
|
|
@@ -12,7 +12,6 @@ module RubyLLM
|
|
|
12
12
|
OPENAI_INLINE_FILE_LIMIT = 50 * 1024 * 1024
|
|
13
13
|
OPENAI_FILE_UPLOAD_LIMIT = 512 * 1024 * 1024
|
|
14
14
|
PROMPT_CACHE_OPTIONS = %i[key ttl mode retention].freeze
|
|
15
|
-
CACHE_BREAKPOINT_ROLES = %i[user system].freeze
|
|
16
15
|
|
|
17
16
|
module_function
|
|
18
17
|
|
|
@@ -47,7 +46,6 @@ module RubyLLM
|
|
|
47
46
|
payload[:reasoning] = { effort: effort } if effort
|
|
48
47
|
payload[:reasoning] = (payload[:reasoning] || {}).merge(summary: 'auto') if thinking&.display == :summarized
|
|
49
48
|
payload.merge!(prompt_cache_params(caching)) if caching
|
|
50
|
-
force_explicit_cache_mode(payload) if caching != false && cache_boundaries?(messages)
|
|
51
49
|
|
|
52
50
|
payload
|
|
53
51
|
end
|
|
@@ -225,14 +223,6 @@ module RubyLLM
|
|
|
225
223
|
retention
|
|
226
224
|
end
|
|
227
225
|
|
|
228
|
-
def force_explicit_cache_mode(payload)
|
|
229
|
-
payload[:prompt_cache_options] = { mode: 'explicit' }.merge(payload[:prompt_cache_options] || {})
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def cache_boundaries?(messages)
|
|
233
|
-
messages.any? { |msg| msg.cache_until_here? && CACHE_BREAKPOINT_ROLES.include?(msg.role) }
|
|
234
|
-
end
|
|
235
|
-
|
|
236
226
|
def prompt_cache_options(caching)
|
|
237
227
|
options = caching.to_h.transform_keys(&:to_sym)
|
|
238
228
|
unsupported = options.keys - PROMPT_CACHE_OPTIONS
|
|
@@ -8,7 +8,6 @@ module RubyLLM
|
|
|
8
8
|
# instead of summaries, both in responses and in the stream.
|
|
9
9
|
class Responses < Protocols::Responses
|
|
10
10
|
SERVER_TOOL_ALIASES = {
|
|
11
|
-
web_search: { tool: { type: 'web_search' } },
|
|
12
11
|
apply_patch: { tool: { type: 'custom', name: 'apply_patch' } }
|
|
13
12
|
}.freeze
|
|
14
13
|
|
|
@@ -22,7 +22,7 @@ module RubyLLM
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def ocr_document_part(attachment)
|
|
25
|
-
reference = attachment.url? ? attachment.source.to_s : attachment
|
|
25
|
+
reference = attachment.url? ? attachment.source.to_s : ocr_data_uri(attachment)
|
|
26
26
|
|
|
27
27
|
if attachment.image?
|
|
28
28
|
{ type: 'image_url', image_url: reference }
|
|
@@ -31,6 +31,10 @@ module RubyLLM
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
def ocr_data_uri(attachment)
|
|
35
|
+
"data:#{attachment.mime_type};base64,#{attachment.encoded}"
|
|
36
|
+
end
|
|
37
|
+
|
|
34
38
|
def parse_ocr_response(response, model:)
|
|
35
39
|
data = response.body
|
|
36
40
|
|
|
@@ -47,7 +47,7 @@ module RubyLLM
|
|
|
47
47
|
|
|
48
48
|
reasoning = build_reasoning(thinking)
|
|
49
49
|
payload[:reasoning] = reasoning if reasoning
|
|
50
|
-
payload[:cache_control] = prompt_cache_control(caching) if caching
|
|
50
|
+
payload[:cache_control] = prompt_cache_control(caching) if caching
|
|
51
51
|
payload
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -142,10 +142,6 @@ module RubyLLM
|
|
|
142
142
|
keys.map { |key| ":#{key}" }.join(', ')
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
def cache_boundaries?(messages)
|
|
146
|
-
messages.any?(&:cache_until_here?)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
145
|
def openai_prompt_caching?
|
|
150
146
|
false
|
|
151
147
|
end
|
data/lib/ruby_llm/rerank.rb
CHANGED
data/lib/ruby_llm/speech.rb
CHANGED
|
@@ -132,7 +132,7 @@ module RubyLLM
|
|
|
132
132
|
empty_tokens = Tokens.new
|
|
133
133
|
payload = {
|
|
134
134
|
provider: provider_instance.slug,
|
|
135
|
-
provider_class: provider_instance.
|
|
135
|
+
provider_class: provider_instance.name,
|
|
136
136
|
model: model.id,
|
|
137
137
|
model_info: model,
|
|
138
138
|
language: language,
|
data/lib/ruby_llm/version.rb
CHANGED
data/lib/ruby_llm/video_job.rb
CHANGED
data/lib/ruby_llm.rb
CHANGED
|
@@ -44,7 +44,7 @@ loader.setup
|
|
|
44
44
|
|
|
45
45
|
# RubyLLM is an AI framework for Ruby and Rails. Build conversations and
|
|
46
46
|
# agents, generate media, process documents, and work with model providers
|
|
47
|
-
# through one Ruby API. The guides at https://rubyllm.com/
|
|
47
|
+
# through one Ruby API. The guides at https://rubyllm.com/ introduce
|
|
48
48
|
# each feature; this reference documents its classes, arguments, and results.
|
|
49
49
|
#
|
|
50
50
|
# RubyLLM.configure do |config|
|
data/lib/tasks/ruby_llm.rake
CHANGED
|
@@ -7,7 +7,7 @@ namespace :ruby_llm do
|
|
|
7
7
|
task action => :environment do
|
|
8
8
|
require 'generators/ruby_llm/upgrade/upgrade_migration'
|
|
9
9
|
|
|
10
|
-
RubyLLM::Generators::UpgradeMigration.
|
|
10
|
+
RubyLLM::Generators::UpgradeMigration.for.public_send(action)
|
|
11
11
|
puts "RubyLLM copy upgrade: #{action} completed"
|
|
12
12
|
end
|
|
13
13
|
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: 2.0.0.
|
|
4
|
+
version: 2.0.0.rc4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carmine Paolino
|
|
@@ -275,6 +275,11 @@ files:
|
|
|
275
275
|
- lib/generators/ruby_llm/tool/templates/tool_call.html.erb.tt
|
|
276
276
|
- lib/generators/ruby_llm/tool/templates/tool_result.html.erb.tt
|
|
277
277
|
- lib/generators/ruby_llm/tool/tool_generator.rb
|
|
278
|
+
- lib/generators/ruby_llm/upgrade/legacy_content_sql.rb
|
|
279
|
+
- lib/generators/ruby_llm/upgrade/online_copy_migration.rb
|
|
280
|
+
- lib/generators/ruby_llm/upgrade/online_copy_migration/data.rb
|
|
281
|
+
- lib/generators/ruby_llm/upgrade/online_copy_migration/journal.rb
|
|
282
|
+
- lib/generators/ruby_llm/upgrade/online_copy_migration/verification.rb
|
|
278
283
|
- lib/generators/ruby_llm/upgrade/templates/backfill_v2_data.rb.tt
|
|
279
284
|
- lib/generators/ruby_llm/upgrade/templates/cleanup_v2_upgrade.rb.tt
|
|
280
285
|
- lib/generators/ruby_llm/upgrade/templates/finish_v2_upgrade.rb.tt
|
|
@@ -607,24 +612,24 @@ metadata:
|
|
|
607
612
|
homepage_uri: https://rubyllm.com
|
|
608
613
|
source_code_uri: https://github.com/crmne/ruby_llm
|
|
609
614
|
changelog_uri: https://github.com/crmne/ruby_llm/releases
|
|
610
|
-
documentation_uri: https://rubyllm.com/
|
|
615
|
+
documentation_uri: https://rubyllm.com/
|
|
611
616
|
bug_tracker_uri: https://github.com/crmne/ruby_llm/issues
|
|
612
617
|
funding_uri: https://github.com/sponsors/crmne
|
|
613
618
|
rubygems_mfa_required: 'true'
|
|
614
619
|
post_install_message: |
|
|
615
|
-
RubyLLM 2.0.0.
|
|
620
|
+
RubyLLM 2.0.0.rc4
|
|
616
621
|
|
|
617
622
|
2.0 renames several APIs and changes what message content returns. Coming
|
|
618
623
|
from 1.x? Read the upgrade guide before you boot:
|
|
619
624
|
|
|
620
|
-
https://rubyllm.com/
|
|
625
|
+
https://rubyllm.com/upgrading/
|
|
621
626
|
|
|
622
627
|
The Rails upgrade uses forward-only preparation, backfill, and finish
|
|
623
628
|
phases, with cleanup later. Rename mode is the default; optional copy
|
|
624
629
|
mode supports a controlled return to 1.16. Read its requirements in the
|
|
625
630
|
upgrade guide and rehearse on a recent production snapshot.
|
|
626
631
|
|
|
627
|
-
Agent skill: https://rubyllm.com/
|
|
632
|
+
Agent skill: https://rubyllm.com/ai-coding-assistants/
|
|
628
633
|
rdoc_options: []
|
|
629
634
|
require_paths:
|
|
630
635
|
- lib
|
|
@@ -639,7 +644,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
639
644
|
- !ruby/object:Gem::Version
|
|
640
645
|
version: '0'
|
|
641
646
|
requirements: []
|
|
642
|
-
rubygems_version: 4.0.
|
|
647
|
+
rubygems_version: 4.0.20
|
|
643
648
|
specification_version: 4
|
|
644
649
|
summary: 'Build AI features the Ruby way: a delightful Ruby AI framework for every
|
|
645
650
|
major AI provider.'
|