ruby_llm 1.4.0 → 1.5.1

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.
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ module Perplexity
6
+ # Models methods of the Perplexity API integration
7
+ module Models
8
+ def list_models(**)
9
+ slug = 'perplexity'
10
+ capabilities = Perplexity::Capabilities
11
+ parse_list_models_response(nil, slug, capabilities)
12
+ end
13
+
14
+ def parse_list_models_response(_response, slug, capabilities)
15
+ [
16
+ create_model_info('sonar', slug, capabilities),
17
+ create_model_info('sonar-pro', slug, capabilities),
18
+ create_model_info('sonar-reasoning', slug, capabilities),
19
+ create_model_info('sonar-reasoning-pro', slug, capabilities),
20
+ create_model_info('sonar-deep-research', slug, capabilities)
21
+ ]
22
+ end
23
+
24
+ def create_model_info(id, slug, capabilities)
25
+ Model::Info.new(
26
+ id: id,
27
+ name: capabilities.format_display_name(id),
28
+ provider: slug,
29
+ family: capabilities.model_family(id).to_s,
30
+ created_at: Time.now,
31
+ context_window: capabilities.context_window_for(id),
32
+ max_output_tokens: capabilities.max_tokens_for(id),
33
+ modalities: capabilities.modalities_for(id),
34
+ capabilities: capabilities.capabilities_for(id),
35
+ pricing: capabilities.pricing_for(id),
36
+ metadata: {}
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyLLM
4
+ module Providers
5
+ # Perplexity API integration.
6
+ module Perplexity
7
+ extend OpenAI
8
+ extend Perplexity::Chat
9
+ extend Perplexity::Models
10
+
11
+ module_function
12
+
13
+ def api_base(_config)
14
+ 'https://api.perplexity.ai'
15
+ end
16
+
17
+ def headers(config)
18
+ {
19
+ 'Authorization' => "Bearer #{config.perplexity_api_key}",
20
+ 'Content-Type' => 'application/json'
21
+ }
22
+ end
23
+
24
+ def capabilities
25
+ Perplexity::Capabilities
26
+ end
27
+
28
+ def slug
29
+ 'perplexity'
30
+ end
31
+
32
+ def configuration_requirements
33
+ %i[perplexity_api_key]
34
+ end
35
+
36
+ def parse_error(response)
37
+ body = response.body
38
+ return if body.empty?
39
+
40
+ # If response is HTML (Perplexity returns HTML for auth errors)
41
+ if body.include?('<html>') && body.include?('<title>')
42
+ # Extract title content
43
+ title_match = body.match(%r{<title>(.+?)</title>})
44
+ if title_match
45
+ # Clean up the title - remove status code if present
46
+ message = title_match[1]
47
+ message = message.sub(/^\d+\s+/, '') # Remove leading digits and space
48
+ return message
49
+ end
50
+ end
51
+
52
+ # Fall back to parent's implementation
53
+ super
54
+ end
55
+ end
56
+ end
57
+ end
@@ -24,6 +24,18 @@ module RubyLLM
24
24
  end
25
25
  end
26
26
 
27
+ def to_time(value)
28
+ return unless value
29
+
30
+ value.is_a?(Time) ? value : Time.parse(value.to_s)
31
+ end
32
+
33
+ def to_date(value)
34
+ return unless value
35
+
36
+ value.is_a?(Date) ? value : Date.parse(value.to_s)
37
+ end
38
+
27
39
  def deep_merge(params, payload)
28
40
  params.merge(payload) do |_key, params_value, payload_value|
29
41
  if params_value.is_a?(Hash) && payload_value.is_a?(Hash)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RubyLLM
4
- VERSION = '1.4.0'
4
+ VERSION = '1.5.1'
5
5
  end
data/lib/ruby_llm.rb CHANGED
@@ -16,9 +16,11 @@ loader.inflector.inflect(
16
16
  'openai' => 'OpenAI',
17
17
  'api' => 'API',
18
18
  'deepseek' => 'DeepSeek',
19
+ 'perplexity' => 'Perplexity',
19
20
  'bedrock' => 'Bedrock',
20
21
  'openrouter' => 'OpenRouter',
21
22
  'gpustack' => 'GPUStack',
23
+ 'mistral' => 'Mistral',
22
24
  'pdf' => 'PDF'
23
25
  )
24
26
  loader.ignore("#{__dir__}/tasks")
@@ -78,14 +80,16 @@ module RubyLLM
78
80
  end
79
81
  end
80
82
 
81
- RubyLLM::Provider.register :openai, RubyLLM::Providers::OpenAI
82
83
  RubyLLM::Provider.register :anthropic, RubyLLM::Providers::Anthropic
83
- RubyLLM::Provider.register :gemini, RubyLLM::Providers::Gemini
84
- RubyLLM::Provider.register :deepseek, RubyLLM::Providers::DeepSeek
85
84
  RubyLLM::Provider.register :bedrock, RubyLLM::Providers::Bedrock
86
- RubyLLM::Provider.register :openrouter, RubyLLM::Providers::OpenRouter
87
- RubyLLM::Provider.register :ollama, RubyLLM::Providers::Ollama
85
+ RubyLLM::Provider.register :deepseek, RubyLLM::Providers::DeepSeek
86
+ RubyLLM::Provider.register :gemini, RubyLLM::Providers::Gemini
88
87
  RubyLLM::Provider.register :gpustack, RubyLLM::Providers::GPUStack
88
+ RubyLLM::Provider.register :mistral, RubyLLM::Providers::Mistral
89
+ RubyLLM::Provider.register :ollama, RubyLLM::Providers::Ollama
90
+ RubyLLM::Provider.register :openai, RubyLLM::Providers::OpenAI
91
+ RubyLLM::Provider.register :openrouter, RubyLLM::Providers::OpenRouter
92
+ RubyLLM::Provider.register :perplexity, RubyLLM::Providers::Perplexity
89
93
 
90
94
  if defined?(Rails::Railtie)
91
95
  require 'ruby_llm/railtie'
@@ -6,14 +6,14 @@ require 'fileutils'
6
6
  namespace :models do
7
7
  desc 'Generate available models documentation'
8
8
  task :docs do
9
- FileUtils.mkdir_p('docs/guides') # ensure output directory exists
9
+ FileUtils.mkdir_p('docs') # ensure output directory exists
10
10
 
11
11
  # Generate markdown content
12
12
  output = generate_models_markdown
13
13
 
14
14
  # Write the output
15
- File.write('docs/guides/available-models.md', output)
16
- puts 'Generated docs/guides/available-models.md'
15
+ File.write('docs/available-models.md', output)
16
+ puts 'Generated docs/available-models.md'
17
17
  end
18
18
  end
19
19
 
@@ -22,9 +22,8 @@ def generate_models_markdown
22
22
  ---
23
23
  layout: default
24
24
  title: Available Models
25
- parent: Guides
26
- nav_order: 11
27
- permalink: /guides/available-models
25
+ nav_order: 5
26
+ permalink: /available-models
28
27
  description: Browse hundreds of AI models from every major provider. Always up-to-date, automatically generated.
29
28
  ---
30
29
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'dotenv/load'
4
4
  require 'ruby_llm'
5
+ require 'json-schema'
5
6
 
6
7
  task default: ['models:update']
7
8
 
@@ -22,7 +23,9 @@ def configure_from_env
22
23
  config.anthropic_api_key = ENV.fetch('ANTHROPIC_API_KEY', nil)
23
24
  config.gemini_api_key = ENV.fetch('GEMINI_API_KEY', nil)
24
25
  config.deepseek_api_key = ENV.fetch('DEEPSEEK_API_KEY', nil)
26
+ config.perplexity_api_key = ENV.fetch('PERPLEXITY_API_KEY', nil)
25
27
  config.openrouter_api_key = ENV.fetch('OPENROUTER_API_KEY', nil)
28
+ config.mistral_api_key = ENV.fetch('MISTRAL_API_KEY', nil)
26
29
  configure_bedrock(config)
27
30
  config.request_timeout = 30
28
31
  end
@@ -47,6 +50,9 @@ def refresh_models
47
50
  elsif models.all.size == initial_count && initial_count.positive?
48
51
  puts 'Warning: Model list unchanged.'
49
52
  else
53
+ puts 'Validating models...'
54
+ validate_models!(models)
55
+
50
56
  puts "Saving models.json (#{models.all.size} models)"
51
57
  models.save_models
52
58
  end
@@ -54,6 +60,28 @@ def refresh_models
54
60
  @models = models
55
61
  end
56
62
 
63
+ def validate_models!(models)
64
+ schema_path = File.expand_path('../ruby_llm/models_schema.json', __dir__)
65
+ models_data = models.all.map(&:to_h)
66
+
67
+ validation_errors = JSON::Validator.fully_validate(schema_path, models_data)
68
+
69
+ unless validation_errors.empty?
70
+ # Save failed models for inspection
71
+ failed_path = File.expand_path('../ruby_llm/models.failed.json', __dir__)
72
+ File.write(failed_path, JSON.pretty_generate(models_data))
73
+
74
+ puts 'ERROR: Models validation failed:'
75
+ puts "\nValidation errors:"
76
+ validation_errors.first(10).each { |error| puts " - #{error}" }
77
+ puts " ... and #{validation_errors.size - 10} more errors" if validation_errors.size > 10
78
+ puts "-> Failed models saved to: #{failed_path}"
79
+ exit(1)
80
+ end
81
+
82
+ puts '✓ Models validation passed'
83
+ end
84
+
57
85
  def display_model_stats
58
86
  puts "\nModel count:"
59
87
  provider_counts = @models.all.group_by(&:provider).transform_values(&:count)
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.4.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carmine Paolino
@@ -168,6 +168,7 @@ files:
168
168
  - lib/ruby_llm/model/pricing_tier.rb
169
169
  - lib/ruby_llm/models.json
170
170
  - lib/ruby_llm/models.rb
171
+ - lib/ruby_llm/models_schema.json
171
172
  - lib/ruby_llm/provider.rb
172
173
  - lib/ruby_llm/providers/anthropic.rb
173
174
  - lib/ruby_llm/providers/anthropic/capabilities.rb
@@ -204,6 +205,11 @@ files:
204
205
  - lib/ruby_llm/providers/gpustack.rb
205
206
  - lib/ruby_llm/providers/gpustack/chat.rb
206
207
  - lib/ruby_llm/providers/gpustack/models.rb
208
+ - lib/ruby_llm/providers/mistral.rb
209
+ - lib/ruby_llm/providers/mistral/capabilities.rb
210
+ - lib/ruby_llm/providers/mistral/chat.rb
211
+ - lib/ruby_llm/providers/mistral/embeddings.rb
212
+ - lib/ruby_llm/providers/mistral/models.rb
207
213
  - lib/ruby_llm/providers/ollama.rb
208
214
  - lib/ruby_llm/providers/ollama/chat.rb
209
215
  - lib/ruby_llm/providers/ollama/media.rb
@@ -218,6 +224,10 @@ files:
218
224
  - lib/ruby_llm/providers/openai/tools.rb
219
225
  - lib/ruby_llm/providers/openrouter.rb
220
226
  - lib/ruby_llm/providers/openrouter/models.rb
227
+ - lib/ruby_llm/providers/perplexity.rb
228
+ - lib/ruby_llm/providers/perplexity/capabilities.rb
229
+ - lib/ruby_llm/providers/perplexity/chat.rb
230
+ - lib/ruby_llm/providers/perplexity/models.rb
221
231
  - lib/ruby_llm/railtie.rb
222
232
  - lib/ruby_llm/stream_accumulator.rb
223
233
  - lib/ruby_llm/streaming.rb