ruby_llm 1.3.0 → 1.3.2beta1
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 +13 -9
- data/lib/ruby_llm/active_record/acts_as.rb +1 -0
- data/lib/ruby_llm/aliases.json +22 -18
- data/lib/ruby_llm/attachment.rb +1 -1
- data/lib/ruby_llm/chat.rb +22 -12
- data/lib/ruby_llm/configuration.rb +1 -0
- data/lib/ruby_llm/models.json +4036 -3956
- data/lib/ruby_llm/models.rb +2 -1
- data/lib/ruby_llm/provider.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/tools.rb +5 -4
- data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +7 -7
- data/lib/ruby_llm/providers/gemini/embeddings.rb +4 -2
- data/lib/ruby_llm/providers/openai/capabilities.rb +4 -1
- data/lib/ruby_llm/providers/openai/chat.rb +12 -8
- data/lib/ruby_llm/providers/openai/embeddings.rb +4 -3
- data/lib/ruby_llm/tool.rb +8 -8
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/ruby_llm.rb +1 -1
- data/lib/tasks/models_docs.rake +14 -8
- metadata +3 -3
data/lib/ruby_llm/models.rb
CHANGED
@@ -92,7 +92,8 @@ module RubyLLM
|
|
92
92
|
f.response :json, parser_options: { symbolize_names: true }
|
93
93
|
end
|
94
94
|
response = connection.get 'https://api.parsera.org/v1/llm-specs'
|
95
|
-
response.body.map { |data| Model::Info.new(data) }
|
95
|
+
models = response.body.map { |data| Model::Info.new(data) }
|
96
|
+
models.reject { |model| model.provider.nil? || model.id.nil? }
|
96
97
|
end
|
97
98
|
|
98
99
|
def merge_models(provider_models, parsera_models)
|
data/lib/ruby_llm/provider.rb
CHANGED
@@ -34,7 +34,7 @@ module RubyLLM
|
|
34
34
|
def embed(text, model:, connection:, dimensions:)
|
35
35
|
payload = render_embedding_payload(text, model:, dimensions:)
|
36
36
|
response = connection.post(embedding_url(model:), payload)
|
37
|
-
parse_embedding_response(response, model:)
|
37
|
+
parse_embedding_response(response, model:, text:)
|
38
38
|
end
|
39
39
|
|
40
40
|
def paint(prompt, model:, size:, connection:)
|
@@ -14,12 +14,13 @@ module RubyLLM
|
|
14
14
|
def format_tool_call(msg)
|
15
15
|
tool_call = msg.tool_calls.values.first
|
16
16
|
|
17
|
+
content = []
|
18
|
+
content << Media.format_text(msg.content) unless msg.content.nil? || msg.content.empty?
|
19
|
+
content << format_tool_use_block(tool_call)
|
20
|
+
|
17
21
|
{
|
18
22
|
role: 'assistant',
|
19
|
-
content:
|
20
|
-
Media.format_text(msg.content),
|
21
|
-
format_tool_use_block(tool_call)
|
22
|
-
]
|
23
|
+
content:
|
23
24
|
}
|
24
25
|
end
|
25
26
|
|
@@ -24,13 +24,13 @@ module RubyLLM
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def read_prelude(chunk, offset)
|
27
|
-
total_length = chunk[offset...offset + 4].unpack1('N')
|
28
|
-
headers_length = chunk[offset + 4...offset + 8].unpack1('N')
|
27
|
+
total_length = chunk[offset...(offset + 4)].unpack1('N')
|
28
|
+
headers_length = chunk[(offset + 4)...(offset + 8)].unpack1('N')
|
29
29
|
[total_length, headers_length]
|
30
30
|
end
|
31
31
|
|
32
32
|
def valid_lengths?(total_length, headers_length)
|
33
|
-
|
33
|
+
valid_length_constraints?(total_length, headers_length)
|
34
34
|
end
|
35
35
|
|
36
36
|
def calculate_positions(offset, total_length, headers_length)
|
@@ -67,17 +67,17 @@ module RubyLLM
|
|
67
67
|
|
68
68
|
def valid_prelude_at_position?(chunk, pos)
|
69
69
|
lengths = extract_potential_lengths(chunk, pos)
|
70
|
-
|
70
|
+
valid_length_constraints?(*lengths)
|
71
71
|
end
|
72
72
|
|
73
73
|
def extract_potential_lengths(chunk, pos)
|
74
74
|
[
|
75
|
-
chunk[pos...pos + 4].unpack1('N'),
|
76
|
-
chunk[pos + 4...pos + 8].unpack1('N')
|
75
|
+
chunk[pos...(pos + 4)].unpack1('N'),
|
76
|
+
chunk[(pos + 4)...(pos + 8)].unpack1('N')
|
77
77
|
]
|
78
78
|
end
|
79
79
|
|
80
|
-
def
|
80
|
+
def valid_length_constraints?(total_length, headers_length)
|
81
81
|
return false if total_length.nil? || headers_length.nil?
|
82
82
|
return false if total_length <= 0 || total_length > 1_000_000
|
83
83
|
return false if headers_length <= 0 || headers_length >= total_length
|
@@ -15,9 +15,11 @@ module RubyLLM
|
|
15
15
|
{ requests: [text].flatten.map { |t| single_embedding_payload(t, model:, dimensions:) } }
|
16
16
|
end
|
17
17
|
|
18
|
-
def parse_embedding_response(response, model:)
|
18
|
+
def parse_embedding_response(response, model:, text:)
|
19
19
|
vectors = response.body['embeddings']&.map { |e| e['values'] }
|
20
|
-
|
20
|
+
# If we only got one embedding AND the input was a single string (not an array),
|
21
|
+
# return it as a single vector
|
22
|
+
vectors = vectors.first if vectors&.length == 1 && !text.is_a?(Array)
|
21
23
|
|
22
24
|
Embedding.new(vectors:, model:, input_tokens: 0)
|
23
25
|
end
|
@@ -215,10 +215,13 @@ module RubyLLM
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
218
|
-
def normalize_temperature(temperature, model_id)
|
218
|
+
def self.normalize_temperature(temperature, model_id)
|
219
219
|
if model_id.match?(/^o\d/)
|
220
220
|
RubyLLM.logger.debug "Model #{model_id} requires temperature=1.0, ignoring provided value"
|
221
221
|
1.0
|
222
|
+
elsif model_id.match?(/-search/)
|
223
|
+
RubyLLM.logger.debug "Model #{model_id} does not accept temperature parameter, removing"
|
224
|
+
nil
|
222
225
|
else
|
223
226
|
temperature
|
224
227
|
end
|
@@ -12,18 +12,22 @@ module RubyLLM
|
|
12
12
|
module_function
|
13
13
|
|
14
14
|
def render_payload(messages, tools:, temperature:, model:, stream: false)
|
15
|
-
{
|
15
|
+
payload = {
|
16
16
|
model: model,
|
17
17
|
messages: format_messages(messages),
|
18
|
-
temperature: temperature,
|
19
18
|
stream: stream
|
20
|
-
}
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
}
|
20
|
+
|
21
|
+
# Only include temperature if it's not nil (some models don't accept it)
|
22
|
+
payload[:temperature] = temperature unless temperature.nil?
|
23
|
+
|
24
|
+
if tools.any?
|
25
|
+
payload[:tools] = tools.map { |_, tool| tool_for(tool) }
|
26
|
+
payload[:tool_choice] = 'auto'
|
26
27
|
end
|
28
|
+
|
29
|
+
payload[:stream_options] = { include_usage: true } if stream
|
30
|
+
payload
|
27
31
|
end
|
28
32
|
|
29
33
|
def parse_completion_response(response)
|
@@ -19,13 +19,14 @@ module RubyLLM
|
|
19
19
|
}.compact
|
20
20
|
end
|
21
21
|
|
22
|
-
def parse_embedding_response(response, model:)
|
22
|
+
def parse_embedding_response(response, model:, text:)
|
23
23
|
data = response.body
|
24
24
|
input_tokens = data.dig('usage', 'prompt_tokens') || 0
|
25
25
|
vectors = data['data'].map { |d| d['embedding'] }
|
26
26
|
|
27
|
-
# If we only got one embedding
|
28
|
-
|
27
|
+
# If we only got one embedding AND the input was a single string (not an array),
|
28
|
+
# return it as a single vector
|
29
|
+
vectors = vectors.first if vectors.length == 1 && !text.is_a?(Array)
|
29
30
|
|
30
31
|
Embedding.new(vectors:, model:, input_tokens:)
|
31
32
|
end
|
data/lib/ruby_llm/tool.rb
CHANGED
@@ -49,14 +49,14 @@ module RubyLLM
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def name
|
52
|
-
self.class.name
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
52
|
+
klass_name = self.class.name
|
53
|
+
normalized = klass_name.to_s.dup.force_encoding('UTF-8').unicode_normalize(:nfkd)
|
54
|
+
normalized.encode('ASCII', replace: '')
|
55
|
+
.gsub(/[^a-zA-Z0-9_-]/, '-')
|
56
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
57
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
58
|
+
.downcase
|
59
|
+
.delete_suffix('_tool')
|
60
60
|
end
|
61
61
|
|
62
62
|
def description
|
data/lib/ruby_llm/version.rb
CHANGED
data/lib/ruby_llm.rb
CHANGED
data/lib/tasks/models_docs.rake
CHANGED
@@ -23,14 +23,14 @@ def generate_models_markdown
|
|
23
23
|
layout: default
|
24
24
|
title: Available Models
|
25
25
|
parent: Guides
|
26
|
-
nav_order:
|
26
|
+
nav_order: 11
|
27
27
|
permalink: /guides/available-models
|
28
28
|
---
|
29
29
|
|
30
30
|
# Available Models
|
31
31
|
{: .no_toc }
|
32
32
|
|
33
|
-
This guide lists all models available in RubyLLM, automatically generated from the current model registry.
|
33
|
+
This guide lists all models available in RubyLLM, automatically generated from the current [model registry](https://github.com/crmne/ruby_llm/blob/main/lib/ruby_llm/models.json).
|
34
34
|
{: .fs-6 .fw-300 }
|
35
35
|
|
36
36
|
## Table of contents
|
@@ -41,15 +41,21 @@ def generate_models_markdown
|
|
41
41
|
|
42
42
|
---
|
43
43
|
|
44
|
-
##
|
44
|
+
## How Model Data Works
|
45
45
|
|
46
|
-
|
46
|
+
RubyLLM's model registry combines data from multiple sources:
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
- **OpenAI, Anthropic, DeepSeek, Gemini**: Data from [Parsera](https://api.parsera.org/v1/llm-specs)
|
49
|
+
- **OpenRouter**: Direct from OpenRouter's API
|
50
|
+
- **Other providers**: Defined in `capabilities.rb` files
|
51
51
|
|
52
|
-
|
52
|
+
## Contributing Model Updates
|
53
|
+
|
54
|
+
**For major providers** (OpenAI, Anthropic, DeepSeek, Gemini): File issues with [Parsera](https://github.com/parsera-labs/api-llm-specs/issues) for public model data corrections.
|
55
|
+
|
56
|
+
**For other providers**: Edit `lib/ruby_llm/providers/<provider>/capabilities.rb` then run `rake models:update`.
|
57
|
+
|
58
|
+
See the [Contributing Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md) for details.
|
53
59
|
|
54
60
|
## Last Updated
|
55
61
|
{: .d-inline-block }
|
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.3.
|
4
|
+
version: 1.3.2beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
@@ -235,14 +235,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
235
235
|
requirements:
|
236
236
|
- - ">="
|
237
237
|
- !ruby/object:Gem::Version
|
238
|
-
version: 3.1.
|
238
|
+
version: 3.1.3
|
239
239
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
240
240
|
requirements:
|
241
241
|
- - ">="
|
242
242
|
- !ruby/object:Gem::Version
|
243
243
|
version: '0'
|
244
244
|
requirements: []
|
245
|
-
rubygems_version: 3.6.
|
245
|
+
rubygems_version: 3.6.9
|
246
246
|
specification_version: 4
|
247
247
|
summary: A single delightful Ruby way to work with AI.
|
248
248
|
test_files: []
|