ruby_llm 1.5.1 → 1.6.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 +1 -1
- data/lib/ruby_llm/active_record/acts_as.rb +46 -6
- data/lib/ruby_llm/aliases.json +27 -3
- data/lib/ruby_llm/chat.rb +27 -6
- data/lib/ruby_llm/configuration.rb +7 -18
- data/lib/ruby_llm/connection.rb +11 -6
- data/lib/ruby_llm/context.rb +2 -3
- data/lib/ruby_llm/embedding.rb +3 -4
- data/lib/ruby_llm/error.rb +2 -2
- data/lib/ruby_llm/image.rb +3 -4
- data/lib/ruby_llm/message.rb +4 -0
- data/lib/ruby_llm/models.json +7306 -6676
- data/lib/ruby_llm/models.rb +22 -31
- data/lib/ruby_llm/provider.rb +150 -89
- data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -2
- data/lib/ruby_llm/providers/anthropic/chat.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/embeddings.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/models.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/streaming.rb +1 -1
- data/lib/ruby_llm/providers/anthropic/tools.rb +1 -1
- data/lib/ruby_llm/providers/anthropic.rb +17 -22
- data/lib/ruby_llm/providers/bedrock/capabilities.rb +3 -63
- data/lib/ruby_llm/providers/bedrock/chat.rb +5 -4
- data/lib/ruby_llm/providers/bedrock/media.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/models.rb +5 -6
- data/lib/ruby_llm/providers/bedrock/signing.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming/base.rb +5 -4
- data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +1 -1
- data/lib/ruby_llm/providers/bedrock/streaming.rb +1 -1
- data/lib/ruby_llm/providers/bedrock.rb +26 -31
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +16 -57
- data/lib/ruby_llm/providers/deepseek/chat.rb +1 -1
- data/lib/ruby_llm/providers/deepseek.rb +12 -17
- data/lib/ruby_llm/providers/gemini/capabilities.rb +1 -1
- data/lib/ruby_llm/providers/gemini/chat.rb +1 -1
- data/lib/ruby_llm/providers/gemini/embeddings.rb +1 -1
- data/lib/ruby_llm/providers/gemini/images.rb +1 -1
- data/lib/ruby_llm/providers/gemini/media.rb +1 -1
- data/lib/ruby_llm/providers/gemini/models.rb +1 -1
- data/lib/ruby_llm/providers/gemini/streaming.rb +1 -1
- data/lib/ruby_llm/providers/gemini/tools.rb +1 -7
- data/lib/ruby_llm/providers/gemini.rb +18 -23
- data/lib/ruby_llm/providers/gpustack/chat.rb +1 -1
- data/lib/ruby_llm/providers/gpustack/models.rb +1 -1
- data/lib/ruby_llm/providers/gpustack.rb +16 -19
- data/lib/ruby_llm/providers/mistral/capabilities.rb +1 -1
- data/lib/ruby_llm/providers/mistral/chat.rb +1 -1
- data/lib/ruby_llm/providers/mistral/embeddings.rb +1 -1
- data/lib/ruby_llm/providers/mistral/models.rb +1 -1
- data/lib/ruby_llm/providers/mistral.rb +14 -19
- data/lib/ruby_llm/providers/ollama/chat.rb +1 -1
- data/lib/ruby_llm/providers/ollama/media.rb +1 -1
- data/lib/ruby_llm/providers/ollama.rb +13 -18
- data/lib/ruby_llm/providers/openai/capabilities.rb +2 -2
- data/lib/ruby_llm/providers/openai/chat.rb +2 -2
- data/lib/ruby_llm/providers/openai/embeddings.rb +1 -1
- data/lib/ruby_llm/providers/openai/images.rb +1 -1
- data/lib/ruby_llm/providers/openai/media.rb +1 -1
- data/lib/ruby_llm/providers/openai/models.rb +1 -1
- data/lib/ruby_llm/providers/openai/streaming.rb +1 -1
- data/lib/ruby_llm/providers/openai/tools.rb +1 -1
- data/lib/ruby_llm/providers/openai.rb +24 -36
- data/lib/ruby_llm/providers/openrouter/models.rb +1 -1
- data/lib/ruby_llm/providers/openrouter.rb +9 -14
- data/lib/ruby_llm/providers/perplexity/capabilities.rb +1 -30
- data/lib/ruby_llm/providers/perplexity/chat.rb +1 -1
- data/lib/ruby_llm/providers/perplexity/models.rb +1 -1
- data/lib/ruby_llm/providers/perplexity.rb +13 -18
- data/lib/ruby_llm/stream_accumulator.rb +3 -3
- data/lib/ruby_llm/streaming.rb +16 -3
- data/lib/ruby_llm/tool.rb +19 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/lib/tasks/models_docs.rake +18 -11
- data/lib/tasks/models_update.rake +5 -4
- metadata +1 -1
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
|
-
|
5
|
+
class Gemini
|
6
6
|
# Tools methods for the Gemini API implementation
|
7
7
|
module Tools
|
8
8
|
# Format tools for Gemini API
|
@@ -18,26 +18,20 @@ module RubyLLM
|
|
18
18
|
def extract_tool_calls(data)
|
19
19
|
return nil unless data
|
20
20
|
|
21
|
-
# Get the first candidate
|
22
21
|
candidate = data.is_a?(Hash) ? data.dig('candidates', 0) : nil
|
23
22
|
return nil unless candidate
|
24
23
|
|
25
|
-
# Get the parts array from content
|
26
24
|
parts = candidate.dig('content', 'parts')
|
27
25
|
return nil unless parts.is_a?(Array)
|
28
26
|
|
29
|
-
# Find the function call part
|
30
27
|
function_call_part = parts.find { |p| p['functionCall'] }
|
31
28
|
return nil unless function_call_part
|
32
29
|
|
33
|
-
# Get the function call data
|
34
30
|
function_data = function_call_part['functionCall']
|
35
31
|
return nil unless function_data
|
36
32
|
|
37
|
-
# Create a unique ID for the tool call
|
38
33
|
id = SecureRandom.uuid
|
39
34
|
|
40
|
-
# Return the tool call in the expected format
|
41
35
|
{
|
42
36
|
id => ToolCall.new(
|
43
37
|
id: id,
|
@@ -3,38 +3,33 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
5
|
# Native Gemini API implementation
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
extend Gemini::Media
|
6
|
+
class Gemini < Provider
|
7
|
+
include Gemini::Chat
|
8
|
+
include Gemini::Embeddings
|
9
|
+
include Gemini::Images
|
10
|
+
include Gemini::Models
|
11
|
+
include Gemini::Streaming
|
12
|
+
include Gemini::Tools
|
13
|
+
include Gemini::Media
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
def api_base(_config)
|
15
|
+
def api_base
|
19
16
|
'https://generativelanguage.googleapis.com/v1beta'
|
20
17
|
end
|
21
18
|
|
22
|
-
def headers
|
19
|
+
def headers
|
23
20
|
{
|
24
|
-
'x-goog-api-key' => config.gemini_api_key
|
21
|
+
'x-goog-api-key' => @config.gemini_api_key
|
25
22
|
}
|
26
23
|
end
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def slug
|
33
|
-
'gemini'
|
34
|
-
end
|
25
|
+
class << self
|
26
|
+
def capabilities
|
27
|
+
Gemini::Capabilities
|
28
|
+
end
|
35
29
|
|
36
|
-
|
37
|
-
|
30
|
+
def configuration_requirements
|
31
|
+
%i[gemini_api_key]
|
32
|
+
end
|
38
33
|
end
|
39
34
|
end
|
40
35
|
end
|
@@ -3,33 +3,30 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
5
|
# GPUStack API integration based on Ollama.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
extend GPUStack::Models
|
6
|
+
class GPUStack < OpenAI
|
7
|
+
include GPUStack::Chat
|
8
|
+
include GPUStack::Models
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
def api_base(config)
|
14
|
-
config.gpustack_api_base
|
10
|
+
def api_base
|
11
|
+
@config.gpustack_api_base
|
15
12
|
end
|
16
13
|
|
17
|
-
def headers
|
14
|
+
def headers
|
15
|
+
return {} unless @config.gpustack_api_key
|
16
|
+
|
18
17
|
{
|
19
|
-
'Authorization' => "Bearer #{config.gpustack_api_key}"
|
18
|
+
'Authorization' => "Bearer #{@config.gpustack_api_key}"
|
20
19
|
}
|
21
20
|
end
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def local?
|
28
|
-
true
|
29
|
-
end
|
22
|
+
class << self
|
23
|
+
def local?
|
24
|
+
true
|
25
|
+
end
|
30
26
|
|
31
|
-
|
32
|
-
|
27
|
+
def configuration_requirements
|
28
|
+
%i[gpustack_api_base]
|
29
|
+
end
|
33
30
|
end
|
34
31
|
end
|
35
32
|
end
|
@@ -3,34 +3,29 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
5
|
# Mistral API integration.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
extend Mistral::Embeddings
|
6
|
+
class Mistral < OpenAI
|
7
|
+
include Mistral::Chat
|
8
|
+
include Mistral::Models
|
9
|
+
include Mistral::Embeddings
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
def api_base(_config)
|
11
|
+
def api_base
|
15
12
|
'https://api.mistral.ai/v1'
|
16
13
|
end
|
17
14
|
|
18
|
-
def headers
|
15
|
+
def headers
|
19
16
|
{
|
20
|
-
'Authorization' => "Bearer #{config.mistral_api_key}"
|
17
|
+
'Authorization' => "Bearer #{@config.mistral_api_key}"
|
21
18
|
}
|
22
19
|
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def slug
|
29
|
-
'mistral'
|
30
|
-
end
|
21
|
+
class << self
|
22
|
+
def capabilities
|
23
|
+
Mistral::Capabilities
|
24
|
+
end
|
31
25
|
|
32
|
-
|
33
|
-
|
26
|
+
def configuration_requirements
|
27
|
+
%i[mistral_api_key]
|
28
|
+
end
|
34
29
|
end
|
35
30
|
end
|
36
31
|
end
|
@@ -3,31 +3,26 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
5
|
# Ollama API integration.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
extend Ollama::Media
|
6
|
+
class Ollama < OpenAI
|
7
|
+
include Ollama::Chat
|
8
|
+
include Ollama::Media
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
def api_base(config)
|
14
|
-
config.ollama_api_base
|
10
|
+
def api_base
|
11
|
+
@config.ollama_api_base
|
15
12
|
end
|
16
13
|
|
17
|
-
def headers
|
14
|
+
def headers
|
18
15
|
{}
|
19
16
|
end
|
20
17
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def configuration_requirements
|
26
|
-
%i[ollama_api_base]
|
27
|
-
end
|
18
|
+
class << self
|
19
|
+
def configuration_requirements
|
20
|
+
%i[ollama_api_base]
|
21
|
+
end
|
28
22
|
|
29
|
-
|
30
|
-
|
23
|
+
def local?
|
24
|
+
true
|
25
|
+
end
|
31
26
|
end
|
32
27
|
end
|
33
28
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
|
-
|
5
|
+
class OpenAI
|
6
6
|
# Determines capabilities and pricing for OpenAI models
|
7
7
|
module Capabilities
|
8
8
|
module_function
|
@@ -264,7 +264,7 @@ module RubyLLM
|
|
264
264
|
capabilities << 'batch' if model_id.match?(/embedding|batch/)
|
265
265
|
|
266
266
|
# Advanced capabilities
|
267
|
-
capabilities << 'reasoning' if model_id.match?(/
|
267
|
+
capabilities << 'reasoning' if model_id.match?(/o\d|gpt-5|codex/)
|
268
268
|
|
269
269
|
if model_id.match?(/gpt-4-turbo|gpt-4o/)
|
270
270
|
capabilities << 'image_generation' if model_id.match?(/vision/)
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
|
-
|
5
|
+
class OpenAI
|
6
6
|
# Chat methods of the OpenAI API integration
|
7
7
|
module Chat
|
8
8
|
def completion_url
|
@@ -78,7 +78,7 @@ module RubyLLM
|
|
78
78
|
def format_role(role)
|
79
79
|
case role
|
80
80
|
when :system
|
81
|
-
'developer'
|
81
|
+
@config.openai_use_system_role ? 'system' : 'developer'
|
82
82
|
else
|
83
83
|
role.to_s
|
84
84
|
end
|
@@ -5,51 +5,39 @@ module RubyLLM
|
|
5
5
|
# OpenAI API integration. Handles chat completion, function calling,
|
6
6
|
# and OpenAI's unique streaming format. Supports GPT-4, GPT-3.5,
|
7
7
|
# and other OpenAI models.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
base.extend(Provider)
|
20
|
-
base.extend(OpenAI::Chat)
|
21
|
-
base.extend(OpenAI::Embeddings)
|
22
|
-
base.extend(OpenAI::Models)
|
23
|
-
base.extend(OpenAI::Streaming)
|
24
|
-
base.extend(OpenAI::Tools)
|
25
|
-
base.extend(OpenAI::Images)
|
26
|
-
base.extend(OpenAI::Media)
|
8
|
+
class OpenAI < Provider
|
9
|
+
include OpenAI::Chat
|
10
|
+
include OpenAI::Embeddings
|
11
|
+
include OpenAI::Models
|
12
|
+
include OpenAI::Streaming
|
13
|
+
include OpenAI::Tools
|
14
|
+
include OpenAI::Images
|
15
|
+
include OpenAI::Media
|
16
|
+
|
17
|
+
def api_base
|
18
|
+
@config.openai_api_base || 'https://api.openai.com/v1'
|
27
19
|
end
|
28
20
|
|
29
|
-
|
30
|
-
|
31
|
-
def api_base(config)
|
32
|
-
config.openai_api_base || 'https://api.openai.com/v1'
|
33
|
-
end
|
34
|
-
|
35
|
-
def headers(config)
|
21
|
+
def headers
|
36
22
|
{
|
37
|
-
'Authorization' => "Bearer #{config.openai_api_key}",
|
38
|
-
'OpenAI-Organization' => config.openai_organization_id,
|
39
|
-
'OpenAI-Project' => config.openai_project_id
|
23
|
+
'Authorization' => "Bearer #{@config.openai_api_key}",
|
24
|
+
'OpenAI-Organization' => @config.openai_organization_id,
|
25
|
+
'OpenAI-Project' => @config.openai_project_id
|
40
26
|
}.compact
|
41
27
|
end
|
42
28
|
|
43
|
-
def
|
44
|
-
OpenAI::Capabilities
|
29
|
+
def maybe_normalize_temperature(temperature, model_id)
|
30
|
+
OpenAI::Capabilities.normalize_temperature(temperature, model_id)
|
45
31
|
end
|
46
32
|
|
47
|
-
|
48
|
-
|
49
|
-
|
33
|
+
class << self
|
34
|
+
def capabilities
|
35
|
+
OpenAI::Capabilities
|
36
|
+
end
|
50
37
|
|
51
|
-
|
52
|
-
|
38
|
+
def configuration_requirements
|
39
|
+
%i[openai_api_key]
|
40
|
+
end
|
53
41
|
end
|
54
42
|
end
|
55
43
|
end
|
@@ -3,28 +3,23 @@
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
5
|
# OpenRouter API integration.
|
6
|
-
|
7
|
-
|
8
|
-
extend OpenRouter::Models
|
6
|
+
class OpenRouter < OpenAI
|
7
|
+
include OpenRouter::Models
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
def api_base(_config)
|
9
|
+
def api_base
|
13
10
|
'https://openrouter.ai/api/v1'
|
14
11
|
end
|
15
12
|
|
16
|
-
def headers
|
13
|
+
def headers
|
17
14
|
{
|
18
|
-
'Authorization' => "Bearer #{config.openrouter_api_key}"
|
15
|
+
'Authorization' => "Bearer #{@config.openrouter_api_key}"
|
19
16
|
}
|
20
17
|
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def configuration_requirements
|
27
|
-
%i[openrouter_api_key]
|
19
|
+
class << self
|
20
|
+
def configuration_requirements
|
21
|
+
%i[openrouter_api_key]
|
22
|
+
end
|
28
23
|
end
|
29
24
|
end
|
30
25
|
end
|
@@ -2,14 +2,11 @@
|
|
2
2
|
|
3
3
|
module RubyLLM
|
4
4
|
module Providers
|
5
|
-
|
5
|
+
class Perplexity
|
6
6
|
# Determines capabilities and pricing for Perplexity models
|
7
7
|
module Capabilities
|
8
8
|
module_function
|
9
9
|
|
10
|
-
# Returns the context window size for the given model ID
|
11
|
-
# @param model_id [String] the model identifier
|
12
|
-
# @return [Integer] the context window size in tokens
|
13
10
|
def context_window_for(model_id)
|
14
11
|
case model_id
|
15
12
|
when /sonar-pro/ then 200_000
|
@@ -17,9 +14,6 @@ module RubyLLM
|
|
17
14
|
end
|
18
15
|
end
|
19
16
|
|
20
|
-
# Returns the maximum number of tokens that can be generated
|
21
|
-
# @param model_id [String] the model identifier
|
22
|
-
# @return [Integer] the maximum number of tokens
|
23
17
|
def max_tokens_for(model_id)
|
24
18
|
case model_id
|
25
19
|
when /sonar-(?:pro|reasoning-pro)/ then 8_192
|
@@ -27,23 +21,14 @@ module RubyLLM
|
|
27
21
|
end
|
28
22
|
end
|
29
23
|
|
30
|
-
# Returns the price per million tokens for input
|
31
|
-
# @param model_id [String] the model identifier
|
32
|
-
# @return [Float] the price per million tokens in USD
|
33
24
|
def input_price_for(model_id)
|
34
25
|
PRICES.dig(model_family(model_id), :input) || 1.0
|
35
26
|
end
|
36
27
|
|
37
|
-
# Returns the price per million tokens for output
|
38
|
-
# @param model_id [String] the model identifier
|
39
|
-
# @return [Float] the price per million tokens in USD
|
40
28
|
def output_price_for(model_id)
|
41
29
|
PRICES.dig(model_family(model_id), :output) || 1.0
|
42
30
|
end
|
43
31
|
|
44
|
-
# Determines if the model supports vision capabilities
|
45
|
-
# @param model_id [String] the model identifier
|
46
|
-
# @return [Boolean] true if the model supports vision
|
47
32
|
def supports_vision?(model_id)
|
48
33
|
case model_id
|
49
34
|
when /sonar-reasoning-pro/, /sonar-reasoning/, /sonar-pro/, /sonar/ then true
|
@@ -51,21 +36,14 @@ module RubyLLM
|
|
51
36
|
end
|
52
37
|
end
|
53
38
|
|
54
|
-
# Determines if the model supports function calling
|
55
|
-
# @param model_id [String] the model identifier
|
56
|
-
# @return [Boolean] true if the model supports functions
|
57
39
|
def supports_functions?(_model_id)
|
58
40
|
false
|
59
41
|
end
|
60
42
|
|
61
|
-
# Determines if the model supports JSON mode
|
62
43
|
def supports_json_mode?(_model_id)
|
63
44
|
true
|
64
45
|
end
|
65
46
|
|
66
|
-
# Formats the model ID into a human-readable display name
|
67
|
-
# @param model_id [String] the model identifier
|
68
|
-
# @return [String] the formatted display name
|
69
47
|
def format_display_name(model_id)
|
70
48
|
case model_id
|
71
49
|
when 'sonar' then 'Sonar'
|
@@ -80,16 +58,10 @@ module RubyLLM
|
|
80
58
|
end
|
81
59
|
end
|
82
60
|
|
83
|
-
# Returns the model type
|
84
|
-
# @param model_id [String] the model identifier
|
85
|
-
# @return [String] the model type (e.g., 'chat')
|
86
61
|
def model_type(_model_id)
|
87
62
|
'chat'
|
88
63
|
end
|
89
64
|
|
90
|
-
# Returns the model family
|
91
|
-
# @param model_id [String] the model identifier
|
92
|
-
# @return [Symbol] the model family
|
93
65
|
def model_family(model_id)
|
94
66
|
case model_id
|
95
67
|
when 'sonar' then :sonar
|
@@ -123,7 +95,6 @@ module RubyLLM
|
|
123
95
|
output_per_million: prices[:output]
|
124
96
|
}
|
125
97
|
|
126
|
-
# Add special pricing if available
|
127
98
|
standard_pricing[:citation_per_million] = prices[:citation] if prices[:citation]
|
128
99
|
standard_pricing[:reasoning_per_million] = prices[:reasoning] if prices[:reasoning]
|
129
100
|
standard_pricing[:search_per_thousand] = prices[:search_queries] if prices[:search_queries]
|