ruby_llm 1.12.1 → 1.13.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +1 -1
  4. data/lib/generators/ruby_llm/generator_helpers.rb +4 -0
  5. data/lib/generators/ruby_llm/install/install_generator.rb +5 -4
  6. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +1 -1
  7. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +1 -1
  8. data/lib/generators/ruby_llm/install/templates/create_models_migration.rb.tt +1 -6
  9. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +1 -1
  10. data/lib/ruby_llm/active_record/acts_as.rb +8 -2
  11. data/lib/ruby_llm/active_record/acts_as_legacy.rb +29 -0
  12. data/lib/ruby_llm/active_record/chat_methods.rb +35 -4
  13. data/lib/ruby_llm/agent.rb +33 -5
  14. data/lib/ruby_llm/aliases.json +19 -9
  15. data/lib/ruby_llm/chat.rb +107 -11
  16. data/lib/ruby_llm/configuration.rb +18 -0
  17. data/lib/ruby_llm/connection.rb +10 -4
  18. data/lib/ruby_llm/content.rb +0 -2
  19. data/lib/ruby_llm/error.rb +32 -1
  20. data/lib/ruby_llm/message.rb +5 -3
  21. data/lib/ruby_llm/model/info.rb +1 -1
  22. data/lib/ruby_llm/models.json +3535 -2894
  23. data/lib/ruby_llm/models.rb +5 -3
  24. data/lib/ruby_llm/provider.rb +5 -1
  25. data/lib/ruby_llm/providers/anthropic/capabilities.rb +22 -4
  26. data/lib/ruby_llm/providers/anthropic/chat.rb +22 -5
  27. data/lib/ruby_llm/providers/anthropic/models.rb +1 -1
  28. data/lib/ruby_llm/providers/anthropic/tools.rb +20 -0
  29. data/lib/ruby_llm/providers/anthropic.rb +1 -1
  30. data/lib/ruby_llm/providers/azure/chat.rb +1 -1
  31. data/lib/ruby_llm/providers/azure/embeddings.rb +1 -1
  32. data/lib/ruby_llm/providers/azure/models.rb +1 -1
  33. data/lib/ruby_llm/providers/azure.rb +88 -0
  34. data/lib/ruby_llm/providers/bedrock/chat.rb +50 -5
  35. data/lib/ruby_llm/providers/bedrock/models.rb +17 -1
  36. data/lib/ruby_llm/providers/bedrock/streaming.rb +8 -4
  37. data/lib/ruby_llm/providers/bedrock.rb +5 -1
  38. data/lib/ruby_llm/providers/deepseek/capabilities.rb +8 -0
  39. data/lib/ruby_llm/providers/deepseek.rb +1 -1
  40. data/lib/ruby_llm/providers/gemini/capabilities.rb +8 -0
  41. data/lib/ruby_llm/providers/gemini/chat.rb +19 -4
  42. data/lib/ruby_llm/providers/gemini/images.rb +1 -1
  43. data/lib/ruby_llm/providers/gemini/streaming.rb +1 -1
  44. data/lib/ruby_llm/providers/gemini/tools.rb +19 -0
  45. data/lib/ruby_llm/providers/gpustack/capabilities.rb +20 -0
  46. data/lib/ruby_llm/providers/gpustack.rb +4 -0
  47. data/lib/ruby_llm/providers/mistral/capabilities.rb +8 -0
  48. data/lib/ruby_llm/providers/mistral/chat.rb +2 -1
  49. data/lib/ruby_llm/providers/ollama/capabilities.rb +20 -0
  50. data/lib/ruby_llm/providers/ollama.rb +7 -1
  51. data/lib/ruby_llm/providers/openai/capabilities.rb +10 -2
  52. data/lib/ruby_llm/providers/openai/chat.rb +15 -5
  53. data/lib/ruby_llm/providers/openai/media.rb +4 -1
  54. data/lib/ruby_llm/providers/openai/temperature.rb +2 -2
  55. data/lib/ruby_llm/providers/openai/tools.rb +27 -2
  56. data/lib/ruby_llm/providers/openrouter/chat.rb +19 -5
  57. data/lib/ruby_llm/providers/openrouter/images.rb +69 -0
  58. data/lib/ruby_llm/providers/openrouter.rb +31 -1
  59. data/lib/ruby_llm/providers/vertexai/models.rb +1 -1
  60. data/lib/ruby_llm/providers/vertexai.rb +14 -6
  61. data/lib/ruby_llm/stream_accumulator.rb +10 -5
  62. data/lib/ruby_llm/streaming.rb +6 -6
  63. data/lib/ruby_llm/tool.rb +48 -3
  64. data/lib/ruby_llm/version.rb +1 -1
  65. data/lib/tasks/models.rake +33 -7
  66. data/lib/tasks/release.rake +1 -1
  67. data/lib/tasks/ruby_llm.rake +7 -0
  68. data/lib/tasks/vcr.rake +1 -1
  69. metadata +4 -1
@@ -14,13 +14,16 @@ module RubyLLM
14
14
 
15
15
  # Error classes for non-HTTP errors
16
16
  class ConfigurationError < StandardError; end
17
+ class PromptNotFoundError < StandardError; end
17
18
  class InvalidRoleError < StandardError; end
19
+ class InvalidToolChoiceError < StandardError; end
18
20
  class ModelNotFoundError < StandardError; end
19
21
  class UnsupportedAttachmentError < StandardError; end
20
22
 
21
23
  # Error classes for different HTTP status codes
22
24
  class BadRequestError < Error; end
23
25
  class ForbiddenError < Error; end
26
+ class ContextLengthExceededError < Error; end
24
27
  class OverloadedError < Error; end
25
28
  class PaymentRequiredError < Error; end
26
29
  class RateLimitError < Error; end
@@ -42,6 +45,18 @@ module RubyLLM
42
45
  end
43
46
 
44
47
  class << self
48
+ CONTEXT_LENGTH_PATTERNS = [
49
+ /context length/i,
50
+ /context window/i,
51
+ /maximum context/i,
52
+ /request too large/i,
53
+ /too many tokens/i,
54
+ /token count exceeds/i,
55
+ /input[_\s-]?token/i,
56
+ /input or output tokens? must be reduced/i,
57
+ /reduce the length of messages/i
58
+ ].freeze
59
+
45
60
  def parse_error(provider:, response:) # rubocop:disable Metrics/PerceivedComplexity
46
61
  message = provider&.parse_error(response)
47
62
 
@@ -49,6 +64,10 @@ module RubyLLM
49
64
  when 200..399
50
65
  message
51
66
  when 400
67
+ if context_length_exceeded?(message)
68
+ raise ContextLengthExceededError.new(response, message || 'Context length exceeded')
69
+ end
70
+
52
71
  raise BadRequestError.new(response, message || 'Invalid request - please check your input')
53
72
  when 401
54
73
  raise UnauthorizedError.new(response, message || 'Invalid API key - check your credentials')
@@ -58,10 +77,14 @@ module RubyLLM
58
77
  raise ForbiddenError.new(response,
59
78
  message || 'Forbidden - you do not have permission to access this resource')
60
79
  when 429
80
+ if context_length_exceeded?(message)
81
+ raise ContextLengthExceededError.new(response, message || 'Context length exceeded')
82
+ end
83
+
61
84
  raise RateLimitError.new(response, message || 'Rate limit exceeded - please wait a moment')
62
85
  when 500
63
86
  raise ServerError.new(response, message || 'API server error - please try again')
64
- when 502..503
87
+ when 502..504
65
88
  raise ServiceUnavailableError.new(response, message || 'API server unavailable - please try again later')
66
89
  when 529
67
90
  raise OverloadedError.new(response, message || 'Service overloaded - please try again later')
@@ -69,6 +92,14 @@ module RubyLLM
69
92
  raise Error.new(response, message || 'An unknown error occurred')
70
93
  end
71
94
  end
95
+
96
+ private
97
+
98
+ def context_length_exceeded?(message)
99
+ return false if message.to_s.empty?
100
+
101
+ CONTEXT_LENGTH_PATTERNS.any? { |pattern| message.match?(pattern) }
102
+ end
72
103
  end
73
104
  end
74
105
  end
@@ -10,9 +10,9 @@ module RubyLLM
10
10
 
11
11
  def initialize(options = {})
12
12
  @role = options.fetch(:role).to_sym
13
- @content = normalize_content(options.fetch(:content))
14
- @model_id = options[:model_id]
15
13
  @tool_calls = options[:tool_calls]
14
+ @content = normalize_content(options.fetch(:content), role: @role, tool_calls: @tool_calls)
15
+ @model_id = options[:model_id]
16
16
  @tool_call_id = options[:tool_call_id]
17
17
  @tokens = options[:tokens] || Tokens.build(
18
18
  input: options[:input_tokens],
@@ -90,7 +90,9 @@ module RubyLLM
90
90
 
91
91
  private
92
92
 
93
- def normalize_content(content)
93
+ def normalize_content(content, role:, tool_calls:)
94
+ return '' if role == :assistant && content.nil? && tool_calls && !tool_calls.empty?
95
+
94
96
  case content
95
97
  when String then Content.new(content)
96
98
  when Hash then Content.new(content[:text], content)
@@ -24,7 +24,7 @@ module RubyLLM
24
24
  @name = data[:name]
25
25
  @provider = data[:provider]
26
26
  @family = data[:family]
27
- @created_at = Utils.to_time(data[:created_at])
27
+ @created_at = Utils.to_time(data[:created_at])&.utc
28
28
  @context_window = data[:context_window]
29
29
  @max_output_tokens = data[:max_output_tokens]
30
30
  @knowledge_cutoff = Utils.to_date(data[:knowledge_cutoff])