ruby_llm 2.1.0 → 2.1.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 +4 -2
- data/lib/ruby_llm/active_record/chat_methods.rb +7 -3
- data/lib/ruby_llm/chat.rb +5 -1
- data/lib/ruby_llm/mcp/client.rb +17 -4
- data/lib/ruby_llm/protocol.rb +7 -3
- data/lib/ruby_llm/protocols/chat_completions/embedding_batches.rb +21 -0
- data/lib/ruby_llm/providers/bedrock/models.rb +1 -2
- data/lib/ruby_llm/providers/mistral/chat_completions/batches.rb +17 -3
- data/lib/ruby_llm/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8000141c827d6b8f899bcf2ec5a23153ecf83f8196fcd35cee7af8de2f9e58e4
|
|
4
|
+
data.tar.gz: '069b322ad8df77cd46ee2dd9b72ec02578281ff9e6c96b349e46aff911ee85b8'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d651244192242fc5f06cf8126932659b46dec1228ed97e3a9876dd7ee581f8972d42fbaca209698b1c42e1704ebb766ca07b450cfd7651d9192f8d46d3f8c42
|
|
7
|
+
data.tar.gz: 9511e7a1c10a8b73cebbefb59a26be1fd7dcec10c369f57d60b7a348d180a8037700826400d9da41b630d190b940aa73ba7c5cbdb7fbd0f4b6cffbcbe563d732
|
data/README.md
CHANGED
|
@@ -21,6 +21,8 @@ Battle tested at [<picture><source media="(prefers-color-scheme: dark)" srcset="
|
|
|
21
21
|
<a href="https://trendshift.io/repositories/13640" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13640" alt="crmne%2Fruby_llm | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
+
https://github.com/user-attachments/assets/519fcb20-ab8c-4af3-9185-0953b3ba7b81
|
|
25
|
+
|
|
24
26
|
> [!NOTE]
|
|
25
27
|
> Using RubyLLM? [Share your story](https://tally.so/r/3Na02p)! Takes 5 minutes.
|
|
26
28
|
|
|
@@ -34,7 +36,7 @@ Build with the models you want. Move between hosted and local providers without
|
|
|
34
36
|
|
|
35
37
|
## Start with one line. Add files, tools, and agents
|
|
36
38
|
|
|
37
|
-
These examples use **2.1.
|
|
39
|
+
These examples use **2.1.1**. Follow [Getting Started](https://rubyllm.com/getting-started/) to install it and configure the providers you want to try. For 1.x, use the [1.x docs](https://rubyllm.com/v1/).
|
|
38
40
|
|
|
39
41
|
```ruby
|
|
40
42
|
# Just ask
|
|
@@ -249,7 +251,7 @@ Agents, evaluations, workflows, RAG, images, audio, and video. Built in, with us
|
|
|
249
251
|
Install RubyLLM 2.1:
|
|
250
252
|
|
|
251
253
|
```bash
|
|
252
|
-
bundle add ruby_llm --version 2.1.
|
|
254
|
+
bundle add ruby_llm --version 2.1.1
|
|
253
255
|
```
|
|
254
256
|
|
|
255
257
|
Configure a provider in your script, or in `config/initializers/ruby_llm.rb` in Rails:
|
|
@@ -741,10 +741,14 @@ module RubyLLM
|
|
|
741
741
|
end
|
|
742
742
|
|
|
743
743
|
def link_usage_entries(message)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
744
|
+
Usage.transaction(requires_new: true) do
|
|
745
|
+
message.ruby_llm_usage_entries.each do |entry|
|
|
746
|
+
record = usage_records_by_entry[entry]
|
|
747
|
+
record&.update!(message: @message)
|
|
748
|
+
end
|
|
747
749
|
end
|
|
750
|
+
rescue StandardError => e
|
|
751
|
+
RubyLLM.logger.warn("RubyLLM could not link usage to message #{@message.id}: #{e.class}: #{e.message}")
|
|
748
752
|
end
|
|
749
753
|
|
|
750
754
|
def usage_records_by_entry
|
data/lib/ruby_llm/chat.rb
CHANGED
|
@@ -1471,7 +1471,11 @@ module RubyLLM
|
|
|
1471
1471
|
end
|
|
1472
1472
|
|
|
1473
1473
|
def discard_missing_uploads(error)
|
|
1474
|
-
|
|
1474
|
+
upload_messages = messages.map do |message|
|
|
1475
|
+
replacements = message.attachments.filter_map { |attachment| @attachment_replacements[attachment] }
|
|
1476
|
+
message.with_attachments(message.attachments + replacements)
|
|
1477
|
+
end
|
|
1478
|
+
@provider.discard_missing_uploads(upload_messages, error, model: @model, protocol: @protocol)
|
|
1475
1479
|
end
|
|
1476
1480
|
|
|
1477
1481
|
def record_usage_entry(entry)
|
data/lib/ruby_llm/mcp/client.rb
CHANGED
|
@@ -6,8 +6,10 @@ module RubyLLM
|
|
|
6
6
|
# 2026-07-28 revision and falls back to the initialize handshake for
|
|
7
7
|
# servers that predate it, declaring only its extensions so those
|
|
8
8
|
# servers never call back. A server that rejects 2026-07-28 while
|
|
9
|
-
# listing it gets the request once more; one that
|
|
10
|
-
#
|
|
9
|
+
# listing it gets the request once more; one that rejects it while
|
|
10
|
+
# listing a revision from LEGACY_VERSIONS, or without listing any,
|
|
11
|
+
# gets the handshake instead; one that answers the handshake with a
|
|
12
|
+
# revision missing from LEGACY_VERSIONS is disconnected.
|
|
11
13
|
#
|
|
12
14
|
# Servers that predate 2026-07-28 may announce that their tools,
|
|
13
15
|
# prompts, or resources changed on the stream of any request. Those
|
|
@@ -103,7 +105,7 @@ module RubyLLM
|
|
|
103
105
|
result = call('server/discover', timeout: DISCOVERY_TIMEOUT)
|
|
104
106
|
result if Array(result['supportedVersions']).include?(VERSION)
|
|
105
107
|
rescue Error => e
|
|
106
|
-
raise if MODERN_ERRORS.include?(e.code)
|
|
108
|
+
raise if MODERN_ERRORS.include?(e.code) && !predates_revision?(e)
|
|
107
109
|
end
|
|
108
110
|
|
|
109
111
|
def handshake
|
|
@@ -188,8 +190,19 @@ module RubyLLM
|
|
|
188
190
|
end
|
|
189
191
|
|
|
190
192
|
def offers_version?(error)
|
|
193
|
+
modern? && error.code == UNSUPPORTED_VERSION && supported_versions(error).include?(VERSION)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def predates_revision?(error)
|
|
197
|
+
return false unless error.code == UNSUPPORTED_VERSION
|
|
198
|
+
|
|
199
|
+
supported = supported_versions(error)
|
|
200
|
+
supported.empty? || supported.intersect?(LEGACY_VERSIONS)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def supported_versions(error)
|
|
191
204
|
supported = error.data['supported'] if error.data.is_a?(Hash)
|
|
192
|
-
|
|
205
|
+
Array(supported)
|
|
193
206
|
end
|
|
194
207
|
|
|
195
208
|
def message(method, params = {}, id: nil)
|
data/lib/ruby_llm/protocol.rb
CHANGED
|
@@ -753,12 +753,16 @@ module RubyLLM
|
|
|
753
753
|
end
|
|
754
754
|
|
|
755
755
|
def missing_uploads(messages, error, scope)
|
|
756
|
-
uploads = messages
|
|
756
|
+
uploads = attachment_uploads(messages, scope)
|
|
757
|
+
named = uploads.select { |_, upload| names_file?(error, upload.id) }
|
|
758
|
+
named.empty? && error.response&.status == 404 ? uploads : named
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
def attachment_uploads(messages, scope)
|
|
762
|
+
messages.flat_map(&:attachments).uniq(&:object_id).filter_map do |attachment|
|
|
757
763
|
upload = attachment.provider_uploads[scope]
|
|
758
764
|
[attachment, upload] if upload
|
|
759
765
|
end
|
|
760
|
-
named = uploads.select { |_, upload| names_file?(error, upload.id) }
|
|
761
|
-
named.empty? && error.response&.status == 404 ? uploads : named
|
|
762
766
|
end
|
|
763
767
|
|
|
764
768
|
def names_file?(error, id)
|
|
@@ -29,6 +29,27 @@ module RubyLLM
|
|
|
29
29
|
def parse_batch_completion_response(body)
|
|
30
30
|
parse_embedding_response(Response.new(body), model: body['model'], text: nil)
|
|
31
31
|
end
|
|
32
|
+
|
|
33
|
+
def parse_batch_result(line)
|
|
34
|
+
body = line.dig('response', 'body')
|
|
35
|
+
return super unless body.is_a?(Hash) && body['data'].is_a?(Array)
|
|
36
|
+
unless valid_embedding_positions?(body['data'])
|
|
37
|
+
return [batch_result_index(line['custom_id']), nil,
|
|
38
|
+
batch_failure(line['custom_id'], 'Invalid or duplicate embedding record positions')]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
ordered = body.merge('data' => body['data'].sort_by { |row| row['index'] })
|
|
42
|
+
super(line.merge('response' => line['response'].merge('body' => ordered)))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# A row's index names the input its vector belongs to, so rows are put
|
|
46
|
+
# back in that order and positions that are not exactly 0...N fail the
|
|
47
|
+
# request rather than pairing vectors with the wrong texts.
|
|
48
|
+
def valid_embedding_positions?(rows)
|
|
49
|
+
positions = rows.map { |row| row.is_a?(Hash) ? row['index'] : nil }
|
|
50
|
+
|
|
51
|
+
positions.all?(Integer) && positions.sort == (0...positions.size).to_a
|
|
52
|
+
end
|
|
32
53
|
end
|
|
33
54
|
end
|
|
34
55
|
end
|
|
@@ -185,6 +185,7 @@ module RubyLLM
|
|
|
185
185
|
def resolve_registry_id(model_id, models, config)
|
|
186
186
|
region = config.bedrock_region.to_s
|
|
187
187
|
return model_id if region.empty?
|
|
188
|
+
return model_id if region_prefixed?(model_id)
|
|
188
189
|
return model_id if mantle_model_id?(model_id)
|
|
189
190
|
|
|
190
191
|
candidate = registered_profile_candidate(model_id, models, region)
|
|
@@ -197,8 +198,6 @@ module RubyLLM
|
|
|
197
198
|
def registered_profile_candidate(model_id, models, region)
|
|
198
199
|
region_prefix_candidates(region).each do |prefix|
|
|
199
200
|
prefixed = prefixed_with(model_id, prefix)
|
|
200
|
-
next if prefixed == model_id
|
|
201
|
-
|
|
202
201
|
candidate = models.all_including_unlisted.find { |m| m.provider == 'bedrock' && m.id == prefixed }
|
|
203
202
|
return candidate if candidate
|
|
204
203
|
end
|
|
@@ -99,9 +99,13 @@ module RubyLLM
|
|
|
99
99
|
custom_id, shape = line['custom_id'].split(':', 2)
|
|
100
100
|
index = batch_result_index(custom_id)
|
|
101
101
|
response = line['response']
|
|
102
|
+
body = response && response['body']
|
|
102
103
|
|
|
103
|
-
if
|
|
104
|
-
|
|
104
|
+
if body && body['data'].is_a?(Array) && !embedding_positions?(body['data'])
|
|
105
|
+
return [index, nil, batch_failure(line['custom_id'], 'Invalid or duplicate embedding record positions')]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
if body
|
|
105
109
|
[index, parse_mistral_batch_body(body, shape:)]
|
|
106
110
|
else
|
|
107
111
|
[index, nil, batch_failure(line['custom_id'], batch_error_message(line))]
|
|
@@ -111,7 +115,17 @@ module RubyLLM
|
|
|
111
115
|
def parse_mistral_batch_body(body, shape:)
|
|
112
116
|
return parse_completion_body(body, raw: body) unless body['data'].is_a?(Array)
|
|
113
117
|
|
|
114
|
-
|
|
118
|
+
ordered = body.merge('data' => body['data'].sort_by { |row| row['index'] })
|
|
119
|
+
parse_embedding_response(Response.new(ordered), model: body['model'], text: shape == 'array' ? [] : nil)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# A row's index names the input its vector belongs to, so rows are put
|
|
123
|
+
# back in that order and positions that are not exactly 0...N fail the
|
|
124
|
+
# request rather than pairing vectors with the wrong inputs.
|
|
125
|
+
def embedding_positions?(rows)
|
|
126
|
+
positions = rows.map { |row| row.is_a?(Hash) ? row['index'] : nil }
|
|
127
|
+
|
|
128
|
+
positions.all?(Integer) && positions.sort == (0...positions.size).to_a
|
|
115
129
|
end
|
|
116
130
|
end
|
|
117
131
|
end
|
data/lib/ruby_llm/version.rb
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: 2.1.
|
|
4
|
+
version: 2.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carmine Paolino
|
|
@@ -30,6 +30,9 @@ dependencies:
|
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: 1.10.0
|
|
33
|
+
- - "!="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 2.0.0
|
|
33
36
|
type: :runtime
|
|
34
37
|
prerelease: false
|
|
35
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -37,6 +40,9 @@ dependencies:
|
|
|
37
40
|
- - ">="
|
|
38
41
|
- !ruby/object:Gem::Version
|
|
39
42
|
version: 1.10.0
|
|
43
|
+
- - "!="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 2.0.0
|
|
40
46
|
- !ruby/object:Gem::Dependency
|
|
41
47
|
name: faraday-multipart
|
|
42
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -679,7 +685,7 @@ metadata:
|
|
|
679
685
|
funding_uri: https://github.com/sponsors/crmne
|
|
680
686
|
rubygems_mfa_required: 'true'
|
|
681
687
|
post_install_message: |
|
|
682
|
-
RubyLLM 2.1.
|
|
688
|
+
RubyLLM 2.1.1
|
|
683
689
|
|
|
684
690
|
Upgrading? Read the upgrade guide before you boot:
|
|
685
691
|
|