ruby_llm_community 1.1.1 → 1.3.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 +25 -7
  3. data/lib/generators/ruby_llm/chat_ui/chat_ui_generator.rb +127 -67
  4. data/lib/generators/ruby_llm/chat_ui/templates/controllers/chats_controller.rb.tt +12 -12
  5. data/lib/generators/ruby_llm/chat_ui/templates/controllers/messages_controller.rb.tt +7 -7
  6. data/lib/generators/ruby_llm/chat_ui/templates/controllers/models_controller.rb.tt +4 -4
  7. data/lib/generators/ruby_llm/chat_ui/templates/jobs/chat_response_job.rb.tt +6 -6
  8. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_chat.html.erb.tt +4 -4
  9. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/_form.html.erb.tt +5 -5
  10. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/index.html.erb.tt +5 -5
  11. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/new.html.erb.tt +4 -4
  12. data/lib/generators/ruby_llm/chat_ui/templates/views/chats/show.html.erb.tt +8 -8
  13. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_content.html.erb.tt +1 -0
  14. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_form.html.erb.tt +5 -5
  15. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_message.html.erb.tt +9 -6
  16. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/_tool_calls.html.erb.tt +7 -0
  17. data/lib/generators/ruby_llm/chat_ui/templates/views/messages/create.turbo_stream.erb.tt +5 -5
  18. data/lib/generators/ruby_llm/chat_ui/templates/views/models/_model.html.erb.tt +9 -9
  19. data/lib/generators/ruby_llm/chat_ui/templates/views/models/index.html.erb.tt +4 -6
  20. data/lib/generators/ruby_llm/chat_ui/templates/views/models/show.html.erb.tt +11 -11
  21. data/lib/generators/ruby_llm/generator_helpers.rb +131 -87
  22. data/lib/generators/ruby_llm/install/install_generator.rb +75 -73
  23. data/lib/generators/ruby_llm/install/templates/add_references_to_chats_tool_calls_and_messages_migration.rb.tt +9 -0
  24. data/lib/generators/ruby_llm/install/templates/create_chats_migration.rb.tt +0 -1
  25. data/lib/generators/ruby_llm/install/templates/create_messages_migration.rb.tt +3 -3
  26. data/lib/generators/ruby_llm/install/templates/create_tool_calls_migration.rb.tt +0 -1
  27. data/lib/generators/ruby_llm/install/templates/initializer.rb.tt +1 -1
  28. data/lib/generators/ruby_llm/upgrade_to_v1_7/upgrade_to_v1_7_generator.rb +88 -85
  29. data/lib/generators/ruby_llm/upgrade_to_v1_9/templates/add_v1_9_message_columns.rb.tt +15 -0
  30. data/lib/generators/ruby_llm/upgrade_to_v1_9/upgrade_to_v1_9_generator.rb +49 -0
  31. data/lib/ruby_llm/active_record/acts_as.rb +17 -8
  32. data/lib/ruby_llm/active_record/chat_methods.rb +41 -13
  33. data/lib/ruby_llm/active_record/message_methods.rb +11 -2
  34. data/lib/ruby_llm/active_record/model_methods.rb +1 -1
  35. data/lib/ruby_llm/aliases.json +46 -20
  36. data/lib/ruby_llm/attachment.rb +8 -0
  37. data/lib/ruby_llm/chat.rb +13 -2
  38. data/lib/ruby_llm/configuration.rb +10 -1
  39. data/lib/ruby_llm/connection.rb +4 -4
  40. data/lib/ruby_llm/content.rb +23 -0
  41. data/lib/ruby_llm/message.rb +17 -9
  42. data/lib/ruby_llm/model/info.rb +4 -0
  43. data/lib/ruby_llm/models.json +12050 -9940
  44. data/lib/ruby_llm/models.rb +21 -25
  45. data/lib/ruby_llm/moderation.rb +56 -0
  46. data/lib/ruby_llm/provider.rb +29 -1
  47. data/lib/ruby_llm/providers/anthropic/chat.rb +18 -5
  48. data/lib/ruby_llm/providers/anthropic/content.rb +44 -0
  49. data/lib/ruby_llm/providers/anthropic/media.rb +5 -4
  50. data/lib/ruby_llm/providers/anthropic/models.rb +9 -2
  51. data/lib/ruby_llm/providers/anthropic/tools.rb +20 -18
  52. data/lib/ruby_llm/providers/bedrock/media.rb +2 -1
  53. data/lib/ruby_llm/providers/bedrock/streaming/content_extraction.rb +9 -2
  54. data/lib/ruby_llm/providers/gemini/chat.rb +353 -72
  55. data/lib/ruby_llm/providers/gemini/media.rb +59 -1
  56. data/lib/ruby_llm/providers/gemini/tools.rb +146 -25
  57. data/lib/ruby_llm/providers/gemini/transcription.rb +116 -0
  58. data/lib/ruby_llm/providers/gemini.rb +2 -1
  59. data/lib/ruby_llm/providers/gpustack/media.rb +1 -0
  60. data/lib/ruby_llm/providers/ollama/media.rb +1 -0
  61. data/lib/ruby_llm/providers/openai/capabilities.rb +15 -7
  62. data/lib/ruby_llm/providers/openai/chat.rb +7 -3
  63. data/lib/ruby_llm/providers/openai/media.rb +2 -1
  64. data/lib/ruby_llm/providers/openai/moderation.rb +34 -0
  65. data/lib/ruby_llm/providers/openai/streaming.rb +7 -3
  66. data/lib/ruby_llm/providers/openai/tools.rb +34 -12
  67. data/lib/ruby_llm/providers/openai/transcription.rb +70 -0
  68. data/lib/ruby_llm/providers/openai_base.rb +2 -0
  69. data/lib/ruby_llm/providers/red_candle/capabilities.rb +124 -0
  70. data/lib/ruby_llm/providers/red_candle/chat.rb +317 -0
  71. data/lib/ruby_llm/providers/red_candle/models.rb +121 -0
  72. data/lib/ruby_llm/providers/red_candle/streaming.rb +40 -0
  73. data/lib/ruby_llm/providers/red_candle.rb +90 -0
  74. data/lib/ruby_llm/providers/vertexai/transcription.rb +16 -0
  75. data/lib/ruby_llm/providers/vertexai.rb +3 -0
  76. data/lib/ruby_llm/railtie.rb +1 -1
  77. data/lib/ruby_llm/stream_accumulator.rb +8 -12
  78. data/lib/ruby_llm/tool.rb +126 -0
  79. data/lib/ruby_llm/transcription.rb +35 -0
  80. data/lib/ruby_llm/utils.rb +46 -0
  81. data/lib/ruby_llm/version.rb +1 -1
  82. data/lib/ruby_llm_community.rb +38 -1
  83. metadata +35 -3
data/lib/ruby_llm/chat.rb CHANGED
@@ -32,7 +32,7 @@ module RubyLLM
32
32
  end
33
33
 
34
34
  def ask(message = nil, with: nil, &)
35
- add_message role: :user, content: Content.new(message, with)
35
+ add_message role: :user, content: build_content(message, with)
36
36
  complete(&)
37
37
  end
38
38
 
@@ -200,7 +200,8 @@ module RubyLLM
200
200
  @on[:tool_call]&.call(tool_call)
201
201
  result = execute_tool tool_call
202
202
  @on[:tool_result]&.call(result)
203
- content = result.is_a?(Content) ? result : result.to_s
203
+ tool_payload = result.is_a?(Tool::Halt) ? result.content : result
204
+ content = content_like?(tool_payload) ? tool_payload : tool_payload.to_s
204
205
  message = add_message role: :tool, content:, tool_call_id: tool_call.id
205
206
  @on[:end_message]&.call(message)
206
207
 
@@ -215,5 +216,15 @@ module RubyLLM
215
216
  args = tool_call.arguments
216
217
  tool.call(args)
217
218
  end
219
+
220
+ def build_content(message, attachments)
221
+ return message if content_like?(message)
222
+
223
+ Content.new(message, attachments)
224
+ end
225
+
226
+ def content_like?(object)
227
+ object.is_a?(Content) || object.is_a?(Content::Raw)
228
+ end
218
229
  end
219
230
  end
@@ -10,6 +10,7 @@ module RubyLLM
10
10
  :openai_use_system_role,
11
11
  :anthropic_api_key,
12
12
  :gemini_api_key,
13
+ :gemini_api_base,
13
14
  :vertexai_project_id,
14
15
  :vertexai_location,
15
16
  :deepseek_api_key,
@@ -24,11 +25,16 @@ module RubyLLM
24
25
  :gpustack_api_base,
25
26
  :gpustack_api_key,
26
27
  :mistral_api_key,
28
+ # Red Candle configuration
29
+ :red_candle_device,
27
30
  # Default models
28
31
  :default_model,
29
32
  :default_embedding_model,
33
+ :default_moderation_model,
30
34
  :default_image_model,
35
+ :default_transcription_model,
31
36
  # Model registry
37
+ :model_registry_file,
32
38
  :model_registry_class,
33
39
  # Rails integration
34
40
  :use_new_acts_as,
@@ -46,7 +52,7 @@ module RubyLLM
46
52
  :log_stream_debug
47
53
 
48
54
  def initialize
49
- @request_timeout = 120
55
+ @request_timeout = 300
50
56
  @max_retries = 3
51
57
  @retry_interval = 0.1
52
58
  @retry_backoff_factor = 2
@@ -55,8 +61,11 @@ module RubyLLM
55
61
 
56
62
  @default_model = 'gpt-4.1-nano'
57
63
  @default_embedding_model = 'text-embedding-3-small'
64
+ @default_moderation_model = 'omni-moderation-latest'
58
65
  @default_image_model = 'gpt-image-1'
66
+ @default_transcription_model = 'whisper-1'
59
67
 
68
+ @model_registry_file = File.expand_path('models.json', __dir__)
60
69
  @model_registry_class = 'Model'
61
70
  @use_new_acts_as = false
62
71
 
@@ -34,8 +34,7 @@ module RubyLLM
34
34
  end
35
35
 
36
36
  def post(url, payload, &)
37
- body = payload.is_a?(Hash) ? JSON.generate(payload, ascii_only: false) : payload
38
- @connection.post url, body do |req|
37
+ @connection.post url, payload do |req|
39
38
  req.headers.merge! @provider.headers if @provider.respond_to?(:headers)
40
39
  yield req if block_given?
41
40
  end
@@ -66,7 +65,7 @@ module RubyLLM
66
65
  errors: true,
67
66
  headers: false,
68
67
  log_level: :debug do |logger|
69
- logger.filter(%r{[A-Za-z0-9+/=]{100,}}, 'data":"[BASE64 DATA]"')
68
+ logger.filter(%r{[A-Za-z0-9+/=]{100,}}, '[BASE64 DATA]')
70
69
  logger.filter(/[-\d.e,\s]{100,}/, '[EMBEDDINGS ARRAY]')
71
70
  end
72
71
  end
@@ -83,9 +82,10 @@ module RubyLLM
83
82
  end
84
83
 
85
84
  def setup_middleware(faraday)
85
+ faraday.request :multipart
86
86
  faraday.request :json
87
87
  faraday.response :json
88
- faraday.adapter Faraday.default_adapter
88
+ faraday.adapter :net_http
89
89
  faraday.use :llm_errors, provider: @provider
90
90
  end
91
91
 
@@ -53,3 +53,26 @@ module RubyLLM
53
53
  end
54
54
  end
55
55
  end
56
+
57
+ module RubyLLM
58
+ class Content
59
+ # Represents provider-specific payloads that should bypass RubyLLM formatting.
60
+ class Raw
61
+ attr_reader :value
62
+
63
+ def initialize(value)
64
+ raise ArgumentError, 'Raw content payload cannot be nil' if value.nil?
65
+
66
+ @value = value
67
+ end
68
+
69
+ def format
70
+ @value
71
+ end
72
+
73
+ def to_h
74
+ @value
75
+ end
76
+ end
77
+ end
78
+ end
@@ -5,22 +5,22 @@ module RubyLLM
5
5
  class Message
6
6
  ROLES = %i[system user assistant tool].freeze
7
7
 
8
- attr_reader :role, :tool_calls, :tool_call_id, :input_tokens, :output_tokens, :model_id, :raw,
9
- :cached_tokens, :cache_creation_tokens, :reasoning_id
8
+ attr_reader :role, :model_id, :tool_calls, :tool_call_id, :input_tokens, :output_tokens,
9
+ :cached_tokens, :cache_creation_tokens, :raw, :reasoning_id
10
10
  attr_writer :content
11
11
 
12
12
  def initialize(options = {})
13
13
  @role = options.fetch(:role).to_sym
14
14
  @content = normalize_content(options.fetch(:content))
15
+ @model_id = options[:model_id]
15
16
  @tool_calls = options[:tool_calls]
17
+ @tool_call_id = options[:tool_call_id]
16
18
  @input_tokens = options[:input_tokens]
17
19
  @output_tokens = options[:output_tokens]
18
- @model_id = options[:model_id]
19
- @tool_call_id = options[:tool_call_id]
20
20
  @cached_tokens = options[:cached_tokens]
21
21
  @cache_creation_tokens = options[:cache_creation_tokens]
22
- @reasoning_id = options[:reasoning_id]
23
22
  @raw = options[:raw]
23
+ @reasoning_id = options[:reasoning_id]
24
24
 
25
25
  ensure_valid_role
26
26
  end
@@ -46,16 +46,24 @@ module RubyLLM
46
46
  end
47
47
 
48
48
  def to_h
49
+ content_value = content
50
+ content_value = content_value.to_h if content_value.is_a?(Content) || content_value.is_a?(Content::Raw)
51
+
52
+ tool_calls_value = tool_calls
53
+ if tool_calls_value.is_a?(Hash) && tool_calls_value.values.any?(ToolCall)
54
+ tool_calls_value = tool_calls_value&.transform_values(&:to_h)
55
+ end
56
+
49
57
  {
50
58
  role: role,
51
- content: content.is_a?(Content) ? content.to_h : content,
52
- tool_calls: tool_calls,
59
+ content: content_value,
60
+ model_id: model_id,
61
+ tool_calls: tool_calls_value,
53
62
  tool_call_id: tool_call_id,
54
63
  input_tokens: input_tokens,
55
64
  output_tokens: output_tokens,
56
- model_id: model_id,
57
- cache_creation_tokens: cache_creation_tokens,
58
65
  cached_tokens: cached_tokens,
66
+ cache_creation_tokens: cache_creation_tokens,
59
67
  reasoning_id: reasoning_id
60
68
  }.compact
61
69
  end
@@ -72,6 +72,10 @@ module RubyLLM
72
72
  pricing.text_tokens.output
73
73
  end
74
74
 
75
+ def provider_class
76
+ RubyLLM::Provider.resolve provider
77
+ end
78
+
75
79
  def type # rubocop:disable Metrics/PerceivedComplexity
76
80
  if modalities.output.include?('embeddings') && !modalities.output.include?('text')
77
81
  'embedding'