ruby_llm_community 0.0.6 → 1.0.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +34 -0
  4. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +5 -0
  5. data/lib/generators/ruby_llm/install/templates/model_model.rb.tt +6 -0
  6. data/lib/generators/ruby_llm/install_generator.rb +27 -2
  7. data/lib/ruby_llm/active_record/acts_as.rb +163 -24
  8. data/lib/ruby_llm/aliases.json +58 -5
  9. data/lib/ruby_llm/aliases.rb +7 -25
  10. data/lib/ruby_llm/chat.rb +10 -17
  11. data/lib/ruby_llm/configuration.rb +5 -12
  12. data/lib/ruby_llm/connection.rb +4 -4
  13. data/lib/ruby_llm/connection_multipart.rb +19 -0
  14. data/lib/ruby_llm/content.rb +5 -2
  15. data/lib/ruby_llm/embedding.rb +1 -2
  16. data/lib/ruby_llm/error.rb +0 -8
  17. data/lib/ruby_llm/image.rb +23 -8
  18. data/lib/ruby_llm/image_attachment.rb +21 -0
  19. data/lib/ruby_llm/message.rb +6 -6
  20. data/lib/ruby_llm/model/info.rb +12 -10
  21. data/lib/ruby_llm/model/pricing.rb +0 -3
  22. data/lib/ruby_llm/model/pricing_category.rb +0 -2
  23. data/lib/ruby_llm/model/pricing_tier.rb +0 -1
  24. data/lib/ruby_llm/models.json +2147 -470
  25. data/lib/ruby_llm/models.rb +65 -34
  26. data/lib/ruby_llm/provider.rb +8 -8
  27. data/lib/ruby_llm/providers/anthropic/capabilities.rb +1 -46
  28. data/lib/ruby_llm/providers/anthropic/chat.rb +2 -2
  29. data/lib/ruby_llm/providers/anthropic/media.rb +0 -1
  30. data/lib/ruby_llm/providers/anthropic/tools.rb +1 -2
  31. data/lib/ruby_llm/providers/anthropic.rb +1 -2
  32. data/lib/ruby_llm/providers/bedrock/chat.rb +2 -4
  33. data/lib/ruby_llm/providers/bedrock/media.rb +0 -1
  34. data/lib/ruby_llm/providers/bedrock/models.rb +0 -2
  35. data/lib/ruby_llm/providers/bedrock/streaming/base.rb +0 -12
  36. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +0 -7
  37. data/lib/ruby_llm/providers/bedrock/streaming/message_processing.rb +0 -12
  38. data/lib/ruby_llm/providers/bedrock/streaming/payload_processing.rb +0 -12
  39. data/lib/ruby_llm/providers/bedrock/streaming/prelude_handling.rb +0 -13
  40. data/lib/ruby_llm/providers/bedrock/streaming.rb +0 -18
  41. data/lib/ruby_llm/providers/bedrock.rb +1 -2
  42. data/lib/ruby_llm/providers/deepseek/capabilities.rb +1 -2
  43. data/lib/ruby_llm/providers/deepseek/chat.rb +0 -1
  44. data/lib/ruby_llm/providers/gemini/capabilities.rb +28 -100
  45. data/lib/ruby_llm/providers/gemini/chat.rb +57 -29
  46. data/lib/ruby_llm/providers/gemini/embeddings.rb +0 -2
  47. data/lib/ruby_llm/providers/gemini/images.rb +1 -2
  48. data/lib/ruby_llm/providers/gemini/media.rb +0 -1
  49. data/lib/ruby_llm/providers/gemini/models.rb +1 -2
  50. data/lib/ruby_llm/providers/gemini/streaming.rb +15 -1
  51. data/lib/ruby_llm/providers/gemini/tools.rb +0 -5
  52. data/lib/ruby_llm/providers/gpustack/chat.rb +11 -1
  53. data/lib/ruby_llm/providers/gpustack/media.rb +45 -0
  54. data/lib/ruby_llm/providers/gpustack/models.rb +44 -9
  55. data/lib/ruby_llm/providers/gpustack.rb +1 -0
  56. data/lib/ruby_llm/providers/mistral/capabilities.rb +2 -10
  57. data/lib/ruby_llm/providers/mistral/chat.rb +0 -2
  58. data/lib/ruby_llm/providers/mistral/embeddings.rb +0 -3
  59. data/lib/ruby_llm/providers/mistral/models.rb +0 -1
  60. data/lib/ruby_llm/providers/ollama/chat.rb +0 -1
  61. data/lib/ruby_llm/providers/ollama/media.rb +1 -6
  62. data/lib/ruby_llm/providers/ollama/models.rb +36 -0
  63. data/lib/ruby_llm/providers/ollama.rb +1 -0
  64. data/lib/ruby_llm/providers/openai/capabilities.rb +3 -16
  65. data/lib/ruby_llm/providers/openai/chat.rb +1 -3
  66. data/lib/ruby_llm/providers/openai/embeddings.rb +0 -3
  67. data/lib/ruby_llm/providers/openai/images.rb +73 -3
  68. data/lib/ruby_llm/providers/openai/media.rb +0 -1
  69. data/lib/ruby_llm/providers/openai/response.rb +120 -29
  70. data/lib/ruby_llm/providers/openai/response_media.rb +2 -2
  71. data/lib/ruby_llm/providers/openai/streaming.rb +107 -47
  72. data/lib/ruby_llm/providers/openai/tools.rb +1 -1
  73. data/lib/ruby_llm/providers/openai.rb +1 -3
  74. data/lib/ruby_llm/providers/openai_base.rb +2 -2
  75. data/lib/ruby_llm/providers/openrouter/models.rb +1 -16
  76. data/lib/ruby_llm/providers/perplexity/capabilities.rb +0 -1
  77. data/lib/ruby_llm/providers/perplexity/chat.rb +0 -1
  78. data/lib/ruby_llm/providers/perplexity.rb +1 -5
  79. data/lib/ruby_llm/providers/vertexai/chat.rb +14 -0
  80. data/lib/ruby_llm/providers/vertexai/embeddings.rb +32 -0
  81. data/lib/ruby_llm/providers/vertexai/models.rb +130 -0
  82. data/lib/ruby_llm/providers/vertexai/streaming.rb +14 -0
  83. data/lib/ruby_llm/providers/vertexai.rb +55 -0
  84. data/lib/ruby_llm/railtie.rb +0 -1
  85. data/lib/ruby_llm/stream_accumulator.rb +72 -10
  86. data/lib/ruby_llm/streaming.rb +16 -25
  87. data/lib/ruby_llm/tool.rb +2 -19
  88. data/lib/ruby_llm/tool_call.rb +0 -9
  89. data/lib/ruby_llm/version.rb +1 -1
  90. data/lib/ruby_llm_community.rb +5 -3
  91. data/lib/tasks/models.rake +525 -0
  92. data/lib/tasks/release.rake +37 -2
  93. data/lib/tasks/vcr.rake +0 -7
  94. metadata +13 -4
  95. data/lib/tasks/aliases.rake +0 -235
  96. data/lib/tasks/models_docs.rake +0 -224
  97. data/lib/tasks/models_update.rake +0 -108
@@ -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,20 +18,24 @@ module RubyLLM
25
18
  File.expand_path('models.json', __dir__)
26
19
  end
27
20
 
28
- def refresh!
29
- # Collect models from both sources
30
- provider_models = fetch_from_providers
31
- parsera_models = fetch_from_parsera
21
+ def schema_file
22
+ File.expand_path('models_schema.json', __dir__)
23
+ end
32
24
 
33
- # Merge with parsera data taking precedence
25
+ def refresh!(remote_only: false)
26
+ provider_models = fetch_from_providers(remote_only: remote_only)
27
+ parsera_models = fetch_from_parsera
34
28
  merged_models = merge_models(provider_models, parsera_models)
35
-
36
29
  @instance = new(merged_models)
37
30
  end
38
31
 
39
- def fetch_from_providers
32
+ def fetch_from_providers(remote_only: true)
40
33
  config = RubyLLM.config
41
- configured_classes = Provider.configured_remote_providers(config)
34
+ configured_classes = if remote_only
35
+ Provider.configured_remote_providers(config)
36
+ else
37
+ Provider.configured_providers(config)
38
+ end
42
39
  configured = configured_classes.map { |klass| klass.new(config) }
43
40
 
44
41
  RubyLLM.logger.info "Fetching models from providers: #{configured.map(&:name).join(', ')}"
@@ -50,7 +47,6 @@ module RubyLLM
50
47
  config ||= RubyLLM.config
51
48
  provider_class = provider ? Provider.providers[provider.to_sym] : nil
52
49
 
53
- # Check if provider is local
54
50
  if provider_class
55
51
  temp_instance = provider_class.new(config)
56
52
  assume_exists = true if temp_instance.local?
@@ -62,14 +58,15 @@ module RubyLLM
62
58
  provider_class ||= raise(Error, "Unknown provider: #{provider.to_sym}")
63
59
  provider_instance = provider_class.new(config)
64
60
 
65
- model = Model::Info.new(
66
- id: model_id,
67
- name: model_id.tr('-', ' ').capitalize,
68
- provider: provider_instance.slug,
69
- capabilities: %w[function_calling streaming],
70
- modalities: { input: %w[text image], output: %w[text] },
71
- metadata: { warning: 'Assuming model exists, capabilities may not be accurate' }
72
- )
61
+ model = if provider_instance.local?
62
+ begin
63
+ Models.find(model_id, provider)
64
+ rescue ModelNotFoundError
65
+ nil
66
+ end
67
+ end
68
+
69
+ model ||= Model::Info.default(model_id, provider_instance.slug)
73
70
  else
74
71
  model = Models.find model_id, provider
75
72
  provider_class = Provider.providers[model.provider.to_sym] || raise(Error,
@@ -110,20 +107,36 @@ module RubyLLM
110
107
  all_keys = parsera_by_key.keys | provider_by_key.keys
111
108
 
112
109
  models = all_keys.map do |key|
113
- if (parsera_model = parsera_by_key[key])
114
- if (provider_model = provider_by_key[key])
115
- add_provider_metadata(parsera_model, provider_model)
116
- else
117
- parsera_model
118
- end
110
+ parsera_model = find_parsera_model(key, parsera_by_key)
111
+ provider_model = provider_by_key[key]
112
+
113
+ if parsera_model && provider_model
114
+ add_provider_metadata(parsera_model, provider_model)
115
+ elsif parsera_model
116
+ parsera_model
119
117
  else
120
- provider_by_key[key]
118
+ provider_model
121
119
  end
122
120
  end
123
121
 
124
122
  models.sort_by { |m| [m.provider, m.id] }
125
123
  end
126
124
 
125
+ def find_parsera_model(key, parsera_by_key)
126
+ # Direct match
127
+ return parsera_by_key[key] if parsera_by_key[key]
128
+
129
+ # VertexAI uses same models as Gemini
130
+ provider, model_id = key.split(':', 2)
131
+ return unless provider == 'vertexai'
132
+
133
+ gemini_model = parsera_by_key["gemini:#{model_id}"]
134
+ return unless gemini_model
135
+
136
+ # Return Gemini's Parsera data but with VertexAI as provider
137
+ Model::Info.new(gemini_model.to_h.merge(provider: 'vertexai'))
138
+ end
139
+
127
140
  def index_by_key(models)
128
141
  models.each_with_object({}) do |model, hash|
129
142
  hash["#{model.provider}:#{model.id}"] = model
@@ -142,6 +155,24 @@ module RubyLLM
142
155
  end
143
156
 
144
157
  def load_models
158
+ # Try to load from database first if configured
159
+ if RubyLLM.config.model_registry_class
160
+ load_from_database
161
+ else
162
+ load_from_json
163
+ end
164
+ rescue StandardError => e
165
+ RubyLLM.logger.debug "Failed to load models from database: #{e.message}, falling back to JSON"
166
+ load_from_json
167
+ end
168
+
169
+ def load_from_database
170
+ model_class = RubyLLM.config.model_registry_class
171
+ model_class = model_class.constantize if model_class.is_a?(String)
172
+ model_class.all.map(&:to_llm)
173
+ end
174
+
175
+ def load_from_json
145
176
  data = File.exist?(self.class.models_file) ? File.read(self.class.models_file) : '[]'
146
177
  JSON.parse(data, symbolize_names: true).map { |model| Model::Info.new(model) }
147
178
  rescue JSON::ParserError
@@ -192,8 +223,8 @@ module RubyLLM
192
223
  self.class.new(all.select { |m| m.provider == provider.to_s })
193
224
  end
194
225
 
195
- def refresh!
196
- self.class.refresh!
226
+ def refresh!(remote_only: false)
227
+ self.class.refresh!(remote_only: remote_only)
197
228
  end
198
229
 
199
230
  private
@@ -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
 
@@ -75,8 +72,8 @@ module RubyLLM
75
72
  parse_embedding_response(response, model:, text:)
76
73
  end
77
74
 
78
- def paint(prompt, model:, size:)
79
- payload = render_image_payload(prompt, model:, size:)
75
+ def paint(prompt, model:, size:, with:, params:)
76
+ payload = render_image_payload(prompt, model:, size:, with:, params:)
80
77
  response = @connection.post images_url, payload
81
78
  parse_image_response(response, model:)
82
79
  end
@@ -126,6 +123,10 @@ module RubyLLM
126
123
  nil
127
124
  end
128
125
 
126
+ def connection_multipart(config)
127
+ @connection_multipart ||= ConnectionMultipart.new(self, config)
128
+ end
129
+
129
130
  class << self
130
131
  def name
131
132
  to_s.split('::').last
@@ -206,13 +207,12 @@ module RubyLLM
206
207
  raise ConfigurationError, "Missing configuration for #{name}: #{missing.join(', ')}"
207
208
  end
208
209
 
209
- def maybe_normalize_temperature(temperature, _model_id)
210
+ def maybe_normalize_temperature(temperature, _model)
210
211
  temperature
211
212
  end
212
213
 
213
214
  def sync_response(connection, payload, additional_headers = {})
214
215
  response = connection.post completion_url, payload do |req|
215
- # Merge additional headers, with existing headers taking precedence
216
216
  req.headers = additional_headers.merge(req.headers) unless additional_headers.empty?
217
217
  end
218
218
  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
@@ -40,10 +40,10 @@ module RubyLLM
40
40
  end
41
41
 
42
42
  {
43
- model: model,
43
+ model: model.id,
44
44
  messages:,
45
45
  stream: stream,
46
- max_tokens: RubyLLM.models.find(model)&.max_tokens || 4096
46
+ max_tokens: model.max_tokens || 4096
47
47
  }
48
48
  end
49
49
 
@@ -8,7 +8,6 @@ module RubyLLM
8
8
  module_function
9
9
 
10
10
  def format_content(content, cache: false)
11
- # Convert Hash/Array back to JSON string for API
12
11
  return [format_text(content.to_json, cache:)] if content.is_a?(Hash) || content.is_a?(Array)
13
12
  return [format_text(content, cache:)] unless content.is_a?(Content)
14
13
 
@@ -46,7 +46,7 @@ module RubyLLM
46
46
  {
47
47
  type: 'tool_result',
48
48
  tool_use_id: msg.tool_call_id,
49
- content: msg.content
49
+ content: Media.format_content(msg.content)
50
50
  }
51
51
  end
52
52
 
@@ -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
@@ -42,8 +41,7 @@ module RubyLLM
42
41
 
43
42
  def render_payload(messages, tools:, temperature:, model:, stream: false, schema: nil, # rubocop:disable Lint/UnusedMethodArgument,Metrics/ParameterLists
44
43
  cache_prompts: { system: false, user: false, tools: false })
45
- # Hold model_id in instance variable for use in completion_url and stream_url
46
- @model_id = model
44
+ @model_id = model.id
47
45
 
48
46
  system_messages, chat_messages = Anthropic::Chat.separate_messages(messages)
49
47
  system_content = Anthropic::Chat.build_system_content(system_messages, cache: cache_prompts[:system])
@@ -67,7 +65,7 @@ module RubyLLM
67
65
  {
68
66
  anthropic_version: 'bedrock-2023-05-31',
69
67
  messages: messages,
70
- max_tokens: RubyLLM.models.find(model)&.max_tokens || 4096
68
+ max_tokens: model.max_tokens || 4096
71
69
  }
72
70
  end
73
71
  end
@@ -11,7 +11,6 @@ module RubyLLM
11
11
  module_function
12
12
 
13
13
  def format_content(content, cache: false)
14
- # Convert Hash/Array back to JSON string for API
15
14
  return [Anthropic::Media.format_text(content.to_json, cache:)] if content.is_a?(Hash) || content.is_a?(Array)
16
15
  return [Anthropic::Media.format_text(content, cache:)] 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