ruby_llm 1.6.2 → 1.6.3

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +73 -91
  3. data/lib/ruby_llm/active_record/acts_as.rb +2 -10
  4. data/lib/ruby_llm/aliases.json +4 -0
  5. data/lib/ruby_llm/aliases.rb +7 -25
  6. data/lib/ruby_llm/chat.rb +2 -10
  7. data/lib/ruby_llm/configuration.rb +1 -12
  8. data/lib/ruby_llm/content.rb +0 -2
  9. data/lib/ruby_llm/embedding.rb +1 -2
  10. data/lib/ruby_llm/error.rb +0 -8
  11. data/lib/ruby_llm/image.rb +0 -4
  12. data/lib/ruby_llm/message.rb +2 -4
  13. data/lib/ruby_llm/model/info.rb +0 -10
  14. data/lib/ruby_llm/model/pricing.rb +0 -3
  15. data/lib/ruby_llm/model/pricing_category.rb +0 -2
  16. data/lib/ruby_llm/model/pricing_tier.rb +0 -1
  17. data/lib/ruby_llm/models.json +623 -452
  18. data/lib/ruby_llm/models.rb +5 -13
  19. data/lib/ruby_llm/provider.rb +1 -5
  20. data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -46
  21. data/lib/ruby_llm/providers/anthropic/media.rb +0 -1
  22. data/lib/ruby_llm/providers/anthropic/tools.rb +0 -1
  23. data/lib/ruby_llm/providers/anthropic.rb +1 -2
  24. data/lib/ruby_llm/providers/bedrock/chat.rb +0 -2
  25. data/lib/ruby_llm/providers/bedrock/media.rb +0 -1
  26. data/lib/ruby_llm/providers/bedrock/models.rb +0 -2
  27. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -12
  28. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -7
  29. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -12
  30. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -12
  31. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -13
  32. data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -18
  33. data/lib/ruby_llm/providers/bedrock.rb +1 -2
  34. data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -2
  35. data/lib/ruby_llm/providers/deepseek/chat.rb +0 -1
  36. data/lib/ruby_llm/providers/gemini/capabilities.rb +26 -101
  37. data/lib/ruby_llm/providers/gemini/chat.rb +5 -7
  38. data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -2
  39. data/lib/ruby_llm/providers/gemini/images.rb +0 -1
  40. data/lib/ruby_llm/providers/gemini/media.rb +0 -1
  41. data/lib/ruby_llm/providers/gemini/models.rb +1 -2
  42. data/lib/ruby_llm/providers/gemini/tools.rb +0 -5
  43. data/lib/ruby_llm/providers/gpustack/chat.rb +0 -1
  44. data/lib/ruby_llm/providers/gpustack/models.rb +3 -4
  45. data/lib/ruby_llm/providers/mistral/capabilities.rb +2 -10
  46. data/lib/ruby_llm/providers/mistral/chat.rb +0 -2
  47. data/lib/ruby_llm/providers/mistral/embeddings.rb +0 -3
  48. data/lib/ruby_llm/providers/mistral/models.rb +0 -1
  49. data/lib/ruby_llm/providers/ollama/chat.rb +0 -1
  50. data/lib/ruby_llm/providers/ollama/media.rb +0 -1
  51. data/lib/ruby_llm/providers/openai/capabilities.rb +0 -15
  52. data/lib/ruby_llm/providers/openai/chat.rb +0 -3
  53. data/lib/ruby_llm/providers/openai/embeddings.rb +0 -3
  54. data/lib/ruby_llm/providers/openai/media.rb +0 -1
  55. data/lib/ruby_llm/providers/openai.rb +1 -3
  56. data/lib/ruby_llm/providers/openrouter/models.rb +1 -16
  57. data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -1
  58. data/lib/ruby_llm/providers/perplexity/chat.rb +0 -1
  59. data/lib/ruby_llm/providers/perplexity.rb +1 -5
  60. data/lib/ruby_llm/railtie.rb +0 -1
  61. data/lib/ruby_llm/stream_accumulator.rb +1 -3
  62. data/lib/ruby_llm/streaming.rb +15 -24
  63. data/lib/ruby_llm/tool.rb +2 -19
  64. data/lib/ruby_llm/tool_call.rb +0 -9
  65. data/lib/ruby_llm/version.rb +1 -1
  66. data/lib/ruby_llm.rb +0 -2
  67. data/lib/tasks/aliases.rake +5 -35
  68. data/lib/tasks/models_docs.rake +1 -11
  69. data/lib/tasks/models_update.rake +1 -1
  70. data/lib/tasks/vcr.rake +0 -7
  71. metadata +1 -1
@@ -1,14 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- # Registry of available AI models and their capabilities. Provides a clean interface
5
- # to discover and work with models from different providers.
6
- #
7
- # Example:
8
- # RubyLLM.models.all # All available models
9
- # RubyLLM.models.chat_models # Models that support chat
10
- # RubyLLM.models.by_provider('openai').chat_models # OpenAI chat models
11
- # RubyLLM.models.find('claude-3') # Get info about a specific model
4
+ # Registry of available AI models and their capabilities.
12
5
  class Models
13
6
  include Enumerable
14
7
 
@@ -25,14 +18,14 @@ module RubyLLM
25
18
  File.expand_path('models.json', __dir__)
26
19
  end
27
20
 
21
+ def schema_file
22
+ File.expand_path('models_schema.json', __dir__)
23
+ end
24
+
28
25
  def refresh!
29
- # Collect models from both sources
30
26
  provider_models = fetch_from_providers
31
27
  parsera_models = fetch_from_parsera
32
-
33
- # Merge with parsera data taking precedence
34
28
  merged_models = merge_models(provider_models, parsera_models)
35
-
36
29
  @instance = new(merged_models)
37
30
  end
38
31
 
@@ -50,7 +43,6 @@ module RubyLLM
50
43
  config ||= RubyLLM.config
51
44
  provider_class = provider ? Provider.providers[provider.to_sym] : nil
52
45
 
53
- # Check if provider is local
54
46
  if provider_class
55
47
  temp_instance = provider_class.new(config)
56
48
  assume_exists = true if temp_instance.local?
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- # Base class for LLM providers like OpenAI and Anthropic.
5
- # Handles the complexities of API communication, streaming responses,
6
- # and error handling so individual providers can focus on their unique features.
7
- # Encapsulates configuration and connection to eliminate parameter threading.
4
+ # Base class for LLM providers.
8
5
  class Provider
9
6
  include Streaming
10
7
 
@@ -210,7 +207,6 @@ module RubyLLM
210
207
 
211
208
  def sync_response(connection, payload, additional_headers = {})
212
209
  response = connection.post completion_url, payload do |req|
213
- # Merge additional headers, with existing headers taking precedence
214
210
  req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
215
211
  end
216
212
  parse_completion_response response
@@ -7,17 +7,10 @@ module RubyLLM
7
7
  module Capabilities
8
8
  module_function
9
9
 
10
- # Determines the context window size for a given model
11
- # @param model_id [String] the model identifier
12
- # @return [Integer] the context window size in tokens
13
10
  def determine_context_window(_model_id)
14
- # All Claude 3 and 3.5 and 3.7 models have 200K token context windows
15
11
  200_000
16
12
  end
17
13
 
18
- # Determines the maximum output tokens for a given model
19
- # @param model_id [String] the model identifier
20
- # @return [Integer] the maximum output tokens
21
14
  def determine_max_tokens(model_id)
22
15
  case model_id
23
16
  when /claude-3-7-sonnet/, /claude-3-5/ then 8_192
@@ -25,52 +18,30 @@ module RubyLLM
25
18
  end
26
19
  end
27
20
 
28
- # Gets the input price per million tokens for a given model
29
- # @param model_id [String] the model identifier
30
- # @return [Float] the price per million tokens for input
31
21
  def get_input_price(model_id)
32
22
  PRICES.dig(model_family(model_id), :input) || default_input_price
33
23
  end
34
24
 
35
- # Gets the output price per million tokens for a given model
36
- # @param model_id [String] the model identifier
37
- # @return [Float] the price per million tokens for output
38
25
  def get_output_price(model_id)
39
26
  PRICES.dig(model_family(model_id), :output) || default_output_price
40
27
  end
41
28
 
42
- # Determines if a model supports vision capabilities
43
- # @param model_id [String] the model identifier
44
- # @return [Boolean] true if the model supports vision
45
29
  def supports_vision?(model_id)
46
- # All Claude 3, 3.5, and 3.7 models support vision
47
30
  !model_id.match?(/claude-[12]/)
48
31
  end
49
32
 
50
- # Determines if a model supports function calling
51
- # @param model_id [String] the model identifier
52
- # @return [Boolean] true if the model supports functions
53
33
  def supports_functions?(model_id)
54
34
  model_id.match?(/claude-3/)
55
35
  end
56
36
 
57
- # Determines if a model supports JSON mode
58
- # @param model_id [String] the model identifier
59
- # @return [Boolean] true if the model supports JSON mode
60
37
  def supports_json_mode?(model_id)
61
38
  model_id.match?(/claude-3/)
62
39
  end
63
40
 
64
- # Determines if a model supports extended thinking
65
- # @param model_id [String] the model identifier
66
- # @return [Boolean] true if the model supports extended thinking
67
41
  def supports_extended_thinking?(model_id)
68
42
  model_id.match?(/claude-3-7-sonnet/)
69
43
  end
70
44
 
71
- # Determines the model family for a given model ID
72
- # @param model_id [String] the model identifier
73
- # @return [Symbol] the model family identifier
74
45
  def model_family(model_id)
75
46
  case model_id
76
47
  when /claude-3-7-sonnet/ then 'claude-3-7-sonnet'
@@ -83,14 +54,10 @@ module RubyLLM
83
54
  end
84
55
  end
85
56
 
86
- # Returns the model type
87
- # @param model_id [String] the model identifier (unused but kept for API consistency)
88
- # @return [String] the model type, always 'chat' for Anthropic models
89
57
  def model_type(_)
90
58
  'chat'
91
59
  end
92
60
 
93
- # Pricing information for Anthropic models (per million tokens)
94
61
  PRICES = {
95
62
  'claude-3-7-sonnet': { input: 3.0, output: 15.0 },
96
63
  'claude-3-5-sonnet': { input: 3.0, output: 15.0 },
@@ -100,14 +67,10 @@ module RubyLLM
100
67
  'claude-2': { input: 3.0, output: 15.0 }
101
68
  }.freeze
102
69
 
103
- # Default input price if model not found in PRICES
104
- # @return [Float] default price per million tokens for input
105
70
  def default_input_price
106
71
  3.0
107
72
  end
108
73
 
109
- # Default output price if model not found in PRICES
110
- # @return [Float] default price per million tokens for output
111
74
  def default_output_price
112
75
  15.0
113
76
  end
@@ -118,7 +81,6 @@ module RubyLLM
118
81
  output: ['text']
119
82
  }
120
83
 
121
- # All Claude 3+ models support vision
122
84
  unless model_id.match?(/claude-[12]/)
123
85
  modalities[:input] << 'image'
124
86
  modalities[:input] << 'pdf'
@@ -130,18 +92,13 @@ module RubyLLM
130
92
  def capabilities_for(model_id)
131
93
  capabilities = ['streaming']
132
94
 
133
- # Function calling for Claude 3+
134
95
  if model_id.match?(/claude-3/)
135
96
  capabilities << 'function_calling'
136
97
  capabilities << 'batch'
137
98
  end
138
99
 
139
- # Extended thinking (reasoning) for Claude 3.7
140
- capabilities << 'reasoning' if model_id.match?(/claude-3-7/)
141
-
142
- # Citations
100
+ capabilities << 'reasoning' if model_id.match?(/claude-3-7|-4/)
143
101
  capabilities << 'citations' if model_id.match?(/claude-3\.5|claude-3-7/)
144
-
145
102
  capabilities
146
103
  end
147
104
 
@@ -154,13 +111,11 @@ module RubyLLM
154
111
  output_per_million: prices[:output]
155
112
  }
156
113
 
157
- # Batch is typically half the price
158
114
  batch_pricing = {
159
115
  input_per_million: prices[:input] * 0.5,
160
116
  output_per_million: prices[:output] * 0.5
161
117
  }
162
118
 
163
- # Add reasoning output pricing for 3.7 models
164
119
  if model_id.match?(/claude-3-7/)
165
120
  standard_pricing[:reasoning_output_per_million] = prices[:output] * 2.5
166
121
  batch_pricing[:reasoning_output_per_million] = prices[:output] * 1.25
@@ -8,7 +8,6 @@ module RubyLLM
8
8
  module_function
9
9
 
10
10
  def format_content(content)
11
- # Convert Hash/Array back to JSON string for API
12
11
  return [format_text(content.to_json)] if content.is_a?(Hash) || content.is_a?(Array)
13
12
  return [format_text(content)] unless content.is_a?(Content)
14
13
 
@@ -73,7 +73,6 @@ module RubyLLM
73
73
  def parse_tool_calls(content_blocks)
74
74
  return nil if content_blocks.nil?
75
75
 
76
- # Handle single content block (backward compatibility)
77
76
  content_blocks = [content_blocks] unless content_blocks.is_a?(Array)
78
77
 
79
78
  tool_calls = {}
@@ -2,8 +2,7 @@
2
2
 
3
3
  module RubyLLM
4
4
  module Providers
5
- # Anthropic Claude API integration. Handles the complexities of
6
- # Claude's unique message format and tool calling conventions.
5
+ # Anthropic Claude API integration.
7
6
  class Anthropic < Provider
8
7
  include Anthropic::Chat
9
8
  include Anthropic::Embeddings
@@ -11,7 +11,6 @@ module RubyLLM
11
11
  signature = sign_request("#{connection.connection.url_prefix}#{completion_url}", payload:)
12
12
  response = connection.post completion_url, payload do |req|
13
13
  req.headers.merge! build_headers(signature.headers, streaming: block_given?)
14
- # Merge additional headers, with existing headers taking precedence
15
14
  req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
16
15
  end
17
16
  Anthropic::Chat.parse_completion_response response
@@ -41,7 +40,6 @@ module RubyLLM
41
40
  end
42
41
 
43
42
  def render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil) # rubocop:disable Lint/UnusedMethodArgument,Metrics/ParameterLists
44
- # Hold model_id in instance variable for use in completion_url and stream_url
45
43
  @model_id = model
46
44
 
47
45
  system_messages, chat_messages = Anthropic::Chat.separate_messages(messages)
@@ -11,7 +11,6 @@ module RubyLLM
11
11
  module_function
12
12
 
13
13
  def format_content(content)
14
- # Convert Hash/Array back to JSON string for API
15
14
  return [Anthropic::Media.format_text(content.to_json)] if content.is_a?(Hash) || content.is_a?(Array)
16
15
  return [Anthropic::Media.format_text(content)] unless content.is_a?(Content)
17
16
 
@@ -25,7 +25,6 @@ module RubyLLM
25
25
  def parse_list_models_response(response, slug, capabilities)
26
26
  models = Array(response.body['modelSummaries'])
27
27
 
28
- # Filter to include only models we care about
29
28
  models.select { |m| m['modelId'].include?('claude') }.map do |model_data|
30
29
  model_id = model_data['modelId']
31
30
 
@@ -51,7 +50,6 @@ module RubyLLM
51
50
  end
52
51
  end
53
52
 
54
- # Simple test-friendly method that only sets the ID
55
53
  def create_model_info(model_data, slug, _capabilities)
56
54
  model_id = model_data['modelId']
57
55
 
@@ -5,18 +5,6 @@ module RubyLLM
5
5
  class Bedrock
6
6
  module Streaming
7
7
  # Base module for AWS Bedrock streaming functionality.
8
- # Serves as the core module that includes all other streaming-related modules
9
- # and provides fundamental streaming operations.
10
- #
11
- # Responsibilities:
12
- # - Stream URL management
13
- # - Stream handling and error processing
14
- # - Coordinating the functionality of other streaming modules
15
- #
16
- # @example
17
- # module MyStreamingImplementation
18
- # include RubyLLM::Providers::Bedrock::Streaming::Base
19
- # end
20
8
  module Base
21
9
  def self.included(base)
22
10
  base.include ContentExtraction
@@ -5,13 +5,6 @@ module RubyLLM
5
5
  class Bedrock
6
6
  module Streaming
7
7
  # Module for handling content extraction from AWS Bedrock streaming responses.
8
- # Provides methods to extract and process various types of content from the response data.
9
- #
10
- # Responsibilities:
11
- # - Extracting content from different response formats
12
- # - Processing JSON deltas and content blocks
13
- # - Extracting metadata (tokens, model IDs, tool calls)
14
- # - Handling different content structures (arrays, blocks, completions)
15
8
  module ContentExtraction
16
9
  def json_delta?(data)
17
10
  data['type'] == 'content_block_delta' && data.dig('delta', 'type') == 'input_json_delta'
@@ -5,18 +5,6 @@ module RubyLLM
5
5
  class Bedrock
6
6
  module Streaming
7
7
  # Module for processing streaming messages from AWS Bedrock.
8
- # Handles the core message processing logic, including validation and chunking.
9
- #
10
- # Responsibilities:
11
- # - Processing incoming message chunks
12
- # - Validating message structure and content
13
- # - Managing message offsets and boundaries
14
- # - Error handling during message processing
15
- #
16
- # @example Processing a message chunk
17
- # offset = process_message(chunk, current_offset) do |processed_chunk|
18
- # handle_processed_chunk(processed_chunk)
19
- # end
20
8
  module MessageProcessing
21
9
  def process_chunk(chunk, &)
22
10
  offset = 0
@@ -7,18 +7,6 @@ module RubyLLM
7
7
  class Bedrock
8
8
  module Streaming
9
9
  # Module for processing payloads from AWS Bedrock streaming responses.
10
- # Handles JSON payload extraction, decoding, and chunk creation.
11
- #
12
- # Responsibilities:
13
- # - Extracting and validating JSON payloads
14
- # - Decoding Base64-encoded response data
15
- # - Creating response chunks from processed data
16
- # - Error handling for JSON parsing and processing
17
- #
18
- # @example Processing a payload
19
- # process_payload(raw_payload) do |chunk|
20
- # yield_chunk_to_client(chunk)
21
- # end
22
10
  module PayloadProcessing
23
11
  def process_payload(payload, &)
24
12
  json_payload = extract_json_payload(payload)
@@ -5,19 +5,6 @@ module RubyLLM
5
5
  class Bedrock
6
6
  module Streaming
7
7
  # Module for handling message preludes in AWS Bedrock streaming responses.
8
- # Manages the parsing and validation of message headers and prelude data.
9
- #
10
- # Responsibilities:
11
- # - Reading and validating message preludes
12
- # - Calculating message positions and boundaries
13
- # - Finding and validating prelude positions in chunks
14
- # - Ensuring message integrity through length validation
15
- #
16
- # @example Reading a prelude
17
- # if can_read_prelude?(chunk, offset)
18
- # total_length, headers_length = read_prelude(chunk, offset)
19
- # process_message_with_lengths(total_length, headers_length)
20
- # end
21
8
  module PreludeHandling
22
9
  def can_read_prelude?(chunk, offset)
23
10
  chunk.bytesize - offset >= 12
@@ -10,24 +10,6 @@ module RubyLLM
10
10
  module Providers
11
11
  class Bedrock
12
12
  # Streaming implementation for the AWS Bedrock API.
13
- # This module provides functionality for handling streaming responses from AWS Bedrock,
14
- # including message processing, content extraction, and error handling.
15
- #
16
- # The implementation is split into several focused modules:
17
- # - Base: Core streaming functionality and module coordination
18
- # - ContentExtraction: Extracting content from response data
19
- # - MessageProcessing: Processing streaming message chunks
20
- # - PayloadProcessing: Handling JSON payloads and chunk creation
21
- # - PreludeHandling: Managing message preludes and headers
22
- #
23
- # @example Using the streaming module
24
- # class BedrockClient
25
- # include RubyLLM::Providers::Bedrock::Streaming
26
- #
27
- # def stream_response(&block)
28
- # handle_stream(&block)
29
- # end
30
- # end
31
13
  module Streaming
32
14
  include Base
33
15
  end
@@ -5,8 +5,7 @@ require 'time'
5
5
 
6
6
  module RubyLLM
7
7
  module Providers
8
- # AWS Bedrock API integration. Handles chat completion and streaming
9
- # for Claude models.
8
+ # AWS Bedrock API integration.
10
9
  class Bedrock < Provider
11
10
  include Bedrock::Chat
12
11
  include Bedrock::Streaming
@@ -10,7 +10,7 @@ module RubyLLM
10
10
  def context_window_for(model_id)
11
11
  case model_id
12
12
  when /deepseek-(?:chat|reasoner)/ then 64_000
13
- else 32_768 # Sensible default
13
+ else 32_768
14
14
  end
15
15
  end
16
16
 
@@ -67,7 +67,6 @@ module RubyLLM
67
67
  end
68
68
  end
69
69
 
70
- # Pricing information for DeepSeek models (USD per 1M tokens)
71
70
  PRICES = {
72
71
  chat: {
73
72
  input_hit: 0.07,
@@ -8,7 +8,6 @@ module RubyLLM
8
8
  module_function
9
9
 
10
10
  def format_role(role)
11
- # DeepSeek doesn't use the new OpenAI convention for system prompts
12
11
  role.to_s
13
12
  end
14
13
  end