ruby_llm 1.5.0 → 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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/ruby_llm/active_record/acts_as.rb +46 -6
  4. data/lib/ruby_llm/aliases.json +27 -3
  5. data/lib/ruby_llm/chat.rb +27 -6
  6. data/lib/ruby_llm/configuration.rb +7 -18
  7. data/lib/ruby_llm/connection.rb +11 -6
  8. data/lib/ruby_llm/context.rb +2 -3
  9. data/lib/ruby_llm/embedding.rb +3 -4
  10. data/lib/ruby_llm/error.rb +2 -2
  11. data/lib/ruby_llm/image.rb +3 -4
  12. data/lib/ruby_llm/message.rb +4 -0
  13. data/lib/ruby_llm/model/info.rb +2 -2
  14. data/lib/ruby_llm/models.json +7692 -7067
  15. data/lib/ruby_llm/models.rb +22 -31
  16. data/lib/ruby_llm/models_schema.json +168 -0
  17. data/lib/ruby_llm/provider.rb +150 -89
  18. data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -2
  19. data/lib/ruby_llm/providers/anthropic/chat.rb +1 -1
  20. data/lib/ruby_llm/providers/anthropic/embeddings.rb +1 -1
  21. data/lib/ruby_llm/providers/anthropic/media.rb +1 -1
  22. data/lib/ruby_llm/providers/anthropic/models.rb +1 -1
  23. data/lib/ruby_llm/providers/anthropic/streaming.rb +1 -1
  24. data/lib/ruby_llm/providers/anthropic/tools.rb +1 -1
  25. data/lib/ruby_llm/providers/anthropic.rb +17 -22
  26. data/lib/ruby_llm/providers/bedrock/capabilities.rb +3 -63
  27. data/lib/ruby_llm/providers/bedrock/chat.rb +5 -4
  28. data/lib/ruby_llm/providers/bedrock/media.rb +1 -1
  29. data/lib/ruby_llm/providers/bedrock/models.rb +5 -6
  30. data/lib/ruby_llm/providers/bedrock/signing.rb +1 -1
  31. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +5 -4
  32. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +1 -1
  33. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +1 -1
  34. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +1 -1
  35. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +1 -1
  36. data/lib/ruby_llm/providers/bedrock/streaming.rb +1 -1
  37. data/lib/ruby_llm/providers/bedrock.rb +26 -31
  38. data/lib/ruby_llm/providers/deepseek/capabilities.rb +16 -57
  39. data/lib/ruby_llm/providers/deepseek/chat.rb +1 -1
  40. data/lib/ruby_llm/providers/deepseek.rb +12 -17
  41. data/lib/ruby_llm/providers/gemini/capabilities.rb +4 -1
  42. data/lib/ruby_llm/providers/gemini/chat.rb +1 -1
  43. data/lib/ruby_llm/providers/gemini/embeddings.rb +1 -1
  44. data/lib/ruby_llm/providers/gemini/images.rb +1 -1
  45. data/lib/ruby_llm/providers/gemini/media.rb +1 -1
  46. data/lib/ruby_llm/providers/gemini/models.rb +1 -1
  47. data/lib/ruby_llm/providers/gemini/streaming.rb +1 -1
  48. data/lib/ruby_llm/providers/gemini/tools.rb +1 -7
  49. data/lib/ruby_llm/providers/gemini.rb +18 -23
  50. data/lib/ruby_llm/providers/gpustack/chat.rb +1 -1
  51. data/lib/ruby_llm/providers/gpustack/models.rb +1 -1
  52. data/lib/ruby_llm/providers/gpustack.rb +16 -19
  53. data/lib/ruby_llm/providers/mistral/capabilities.rb +31 -19
  54. data/lib/ruby_llm/providers/mistral/chat.rb +1 -1
  55. data/lib/ruby_llm/providers/mistral/embeddings.rb +1 -1
  56. data/lib/ruby_llm/providers/mistral/models.rb +1 -1
  57. data/lib/ruby_llm/providers/mistral.rb +14 -19
  58. data/lib/ruby_llm/providers/ollama/chat.rb +1 -1
  59. data/lib/ruby_llm/providers/ollama/media.rb +1 -1
  60. data/lib/ruby_llm/providers/ollama.rb +13 -18
  61. data/lib/ruby_llm/providers/openai/capabilities.rb +2 -2
  62. data/lib/ruby_llm/providers/openai/chat.rb +2 -2
  63. data/lib/ruby_llm/providers/openai/embeddings.rb +1 -1
  64. data/lib/ruby_llm/providers/openai/images.rb +1 -1
  65. data/lib/ruby_llm/providers/openai/media.rb +1 -1
  66. data/lib/ruby_llm/providers/openai/models.rb +1 -1
  67. data/lib/ruby_llm/providers/openai/streaming.rb +1 -1
  68. data/lib/ruby_llm/providers/openai/tools.rb +1 -1
  69. data/lib/ruby_llm/providers/openai.rb +24 -36
  70. data/lib/ruby_llm/providers/openrouter/models.rb +1 -1
  71. data/lib/ruby_llm/providers/openrouter.rb +9 -14
  72. data/lib/ruby_llm/providers/perplexity/capabilities.rb +1 -30
  73. data/lib/ruby_llm/providers/perplexity/chat.rb +1 -1
  74. data/lib/ruby_llm/providers/perplexity/models.rb +1 -1
  75. data/lib/ruby_llm/providers/perplexity.rb +13 -18
  76. data/lib/ruby_llm/stream_accumulator.rb +3 -3
  77. data/lib/ruby_llm/streaming.rb +16 -3
  78. data/lib/ruby_llm/tool.rb +19 -0
  79. data/lib/ruby_llm/utils.rb +12 -0
  80. data/lib/ruby_llm/version.rb +1 -1
  81. data/lib/tasks/models_docs.rake +18 -11
  82. data/lib/tasks/models_update.rake +31 -4
  83. metadata +2 -1
@@ -3,32 +3,27 @@
3
3
  module RubyLLM
4
4
  module Providers
5
5
  # DeepSeek API integration.
6
- module DeepSeek
7
- extend OpenAI
8
- extend DeepSeek::Chat
6
+ class DeepSeek < OpenAI
7
+ include DeepSeek::Chat
9
8
 
10
- module_function
11
-
12
- def api_base(_config)
9
+ def api_base
13
10
  'https://api.deepseek.com'
14
11
  end
15
12
 
16
- def headers(config)
13
+ def headers
17
14
  {
18
- 'Authorization' => "Bearer #{config.deepseek_api_key}"
15
+ 'Authorization' => "Bearer #{@config.deepseek_api_key}"
19
16
  }
20
17
  end
21
18
 
22
- def capabilities
23
- DeepSeek::Capabilities
24
- end
25
-
26
- def slug
27
- 'deepseek'
28
- end
19
+ class << self
20
+ def capabilities
21
+ DeepSeek::Capabilities
22
+ end
29
23
 
30
- def configuration_requirements
31
- %i[deepseek_api_key]
24
+ def configuration_requirements
25
+ %i[deepseek_api_key]
26
+ end
32
27
  end
33
28
  end
34
29
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Determines capabilities and pricing for Google Gemini models
7
7
  module Capabilities
8
8
  module_function
@@ -280,6 +280,9 @@ module RubyLLM
280
280
  # Embedding output
281
281
  modalities[:output] << 'embeddings' if model_id.match?(/embedding|gemini-embedding/)
282
282
 
283
+ # Image output for imagen models
284
+ modalities[:output] = ['image'] if model_id.match?(/imagen/)
285
+
283
286
  modalities
284
287
  end
285
288
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Chat methods for the Gemini API implementation
7
7
  module Chat
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Embeddings methods for the Gemini API integration
7
7
  module Embeddings
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Image generation methods for the Gemini API implementation
7
7
  module Images
8
8
  def images_url
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Media handling methods for the Gemini API integration
7
7
  module Media
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Models methods for the Gemini API integration
7
7
  module Models
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
5
+ class Gemini
6
6
  # Streaming methods for the Gemini API implementation
7
7
  module Streaming
8
8
  def stream_url
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Gemini
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
- module Gemini
7
- extend Provider
8
- extend Gemini::Chat
9
- extend Gemini::Embeddings
10
- extend Gemini::Images
11
- extend Gemini::Models
12
- extend Gemini::Streaming
13
- extend Gemini::Tools
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
- module_function
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(config)
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
- def capabilities
29
- Gemini::Capabilities
30
- end
31
-
32
- def slug
33
- 'gemini'
34
- end
25
+ class << self
26
+ def capabilities
27
+ Gemini::Capabilities
28
+ end
35
29
 
36
- def configuration_requirements
37
- %i[gemini_api_key]
30
+ def configuration_requirements
31
+ %i[gemini_api_key]
32
+ end
38
33
  end
39
34
  end
40
35
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module GPUStack
5
+ class GPUStack
6
6
  # Chat methods of the GPUStack API integration
7
7
  module Chat
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module GPUStack
5
+ class GPUStack
6
6
  # Models methods of the GPUStack API integration
7
7
  module Models
8
8
  module_function
@@ -3,33 +3,30 @@
3
3
  module RubyLLM
4
4
  module Providers
5
5
  # GPUStack API integration based on Ollama.
6
- module GPUStack
7
- extend OpenAI
8
- extend GPUStack::Chat
9
- extend GPUStack::Models
6
+ class GPUStack < OpenAI
7
+ include GPUStack::Chat
8
+ include GPUStack::Models
10
9
 
11
- module_function
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(config)
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
- def slug
24
- 'gpustack'
25
- end
26
-
27
- def local?
28
- true
29
- end
22
+ class << self
23
+ def local?
24
+ true
25
+ end
30
26
 
31
- def configuration_requirements
32
- %i[gpustack_api_base gpustack_api_key]
27
+ def configuration_requirements
28
+ %i[gpustack_api_base]
29
+ end
33
30
  end
34
31
  end
35
32
  end
@@ -2,25 +2,29 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Mistral
5
+ class Mistral
6
6
  # Determines capabilities for Mistral models
7
7
  module Capabilities
8
8
  module_function
9
9
 
10
- def supports_streaming?(_model_id)
11
- true
10
+ def supports_streaming?(model_id)
11
+ # All chat models support streaming, but not embedding/moderation/OCR/transcription
12
+ !model_id.match?(/embed|moderation|ocr|transcriptions/)
12
13
  end
13
14
 
14
- def supports_tools?(_model_id)
15
- true
15
+ def supports_tools?(model_id)
16
+ # Most chat models support tools except embedding/moderation/OCR/voxtral/transcription
17
+ !model_id.match?(/embed|moderation|ocr|voxtral|transcriptions|mistral-(tiny|small)-(2312|2402)/)
16
18
  end
17
19
 
18
20
  def supports_vision?(model_id)
19
- model_id.include?('pixtral')
21
+ # Models with vision capabilities
22
+ model_id.match?(/pixtral|mistral-small-(2503|2506)|mistral-medium/)
20
23
  end
21
24
 
22
- def supports_json_mode?(_model_id)
23
- true
25
+ def supports_json_mode?(model_id)
26
+ # Most chat models support JSON mode (structured output)
27
+ !model_id.match?(/embed|moderation|ocr|voxtral|transcriptions/) && supports_tools?(model_id)
24
28
  end
25
29
 
26
30
  def format_display_name(model_id)
@@ -71,7 +75,7 @@ module RubyLLM
71
75
  when /embed/
72
76
  {
73
77
  input: ['text'],
74
- output: ['embedding']
78
+ output: ['embeddings']
75
79
  }
76
80
  else
77
81
  {
@@ -81,18 +85,26 @@ module RubyLLM
81
85
  end
82
86
  end
83
87
 
84
- def capabilities_for(model_id)
88
+ def capabilities_for(model_id) # rubocop:disable Metrics/PerceivedComplexity
85
89
  case model_id
86
- when /embed/ then { embeddings: true }
87
- when /moderation/ then { moderation: true }
90
+ when /moderation/ then ['moderation']
91
+ when /voxtral.*transcribe/ then ['transcription']
92
+ when /ocr/ then ['vision']
88
93
  else
89
- {
90
- chat: true,
91
- streaming: supports_streaming?(model_id),
92
- tools: supports_tools?(model_id),
93
- vision: supports_vision?(model_id),
94
- json_mode: supports_json_mode?(model_id)
95
- }
94
+ capabilities = []
95
+ capabilities << 'streaming' if supports_streaming?(model_id)
96
+ capabilities << 'function_calling' if supports_tools?(model_id)
97
+ capabilities << 'structured_output' if supports_json_mode?(model_id)
98
+ capabilities << 'vision' if supports_vision?(model_id)
99
+
100
+ # Model-specific capabilities
101
+ capabilities << 'reasoning' if model_id.match?(/magistral/)
102
+ capabilities << 'batch' unless model_id.match?(/voxtral|ocr|embed|moderation/)
103
+ capabilities << 'fine_tuning' if model_id.match?(/mistral-(small|medium|large)|devstral/)
104
+ capabilities << 'distillation' if model_id.match?(/ministral/)
105
+ capabilities << 'predicted_outputs' if model_id.match?(/codestral/)
106
+
107
+ capabilities.uniq
96
108
  end
97
109
  end
98
110
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Mistral
5
+ class Mistral
6
6
  # Chat methods for Mistral API
7
7
  module Chat
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Mistral
5
+ class Mistral
6
6
  # Embeddings methods for Mistral API
7
7
  module Embeddings
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Mistral
5
+ class Mistral
6
6
  # Model information for Mistral
7
7
  module Models
8
8
  module_function
@@ -3,34 +3,29 @@
3
3
  module RubyLLM
4
4
  module Providers
5
5
  # Mistral API integration.
6
- module Mistral
7
- extend OpenAI
8
- extend Mistral::Chat
9
- extend Mistral::Models
10
- extend Mistral::Embeddings
6
+ class Mistral < OpenAI
7
+ include Mistral::Chat
8
+ include Mistral::Models
9
+ include Mistral::Embeddings
11
10
 
12
- module_function
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(config)
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
- def capabilities
25
- Mistral::Capabilities
26
- end
27
-
28
- def slug
29
- 'mistral'
30
- end
21
+ class << self
22
+ def capabilities
23
+ Mistral::Capabilities
24
+ end
31
25
 
32
- def configuration_requirements
33
- %i[mistral_api_key]
26
+ def configuration_requirements
27
+ %i[mistral_api_key]
28
+ end
34
29
  end
35
30
  end
36
31
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Ollama
5
+ class Ollama
6
6
  # Chat methods of the Ollama API integration
7
7
  module Chat
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module Ollama
5
+ class Ollama
6
6
  # Handles formatting of media content (images, audio) for OpenAI APIs
7
7
  module Media
8
8
  extend OpenAI::Media
@@ -3,31 +3,26 @@
3
3
  module RubyLLM
4
4
  module Providers
5
5
  # Ollama API integration.
6
- module Ollama
7
- extend OpenAI
8
- extend Ollama::Chat
9
- extend Ollama::Media
6
+ class Ollama < OpenAI
7
+ include Ollama::Chat
8
+ include Ollama::Media
10
9
 
11
- module_function
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(_config)
14
+ def headers
18
15
  {}
19
16
  end
20
17
 
21
- def slug
22
- 'ollama'
23
- end
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
- def local?
30
- true
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
- module OpenAI
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?(/o1/)
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
- module OpenAI
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module OpenAI
5
+ class OpenAI
6
6
  # Embeddings methods of the OpenAI API integration
7
7
  module Embeddings
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module OpenAI
5
+ class OpenAI
6
6
  # Image generation methods for the OpenAI API integration
7
7
  module Images
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module OpenAI
5
+ class OpenAI
6
6
  # Handles formatting of media content (images, audio) for OpenAI APIs
7
7
  module Media
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module OpenAI
5
+ class OpenAI
6
6
  # Models methods of the OpenAI API integration
7
7
  module Models
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module OpenAI
5
+ class OpenAI
6
6
  # Streaming methods of the OpenAI API integration
7
7
  module Streaming
8
8
  module_function
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- module OpenAI
5
+ class OpenAI
6
6
  # Tools methods of the OpenAI API integration
7
7
  module Tools
8
8
  module_function