ruby_llm-agents 3.1.0 → 3.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/controllers/ruby_llm/agents/agents_controller.rb +16 -14
  4. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +20 -20
  5. data/app/controllers/ruby_llm/agents/executions_controller.rb +5 -7
  6. data/app/helpers/ruby_llm/agents/application_helper.rb +57 -58
  7. data/app/models/ruby_llm/agents/execution/analytics.rb +27 -27
  8. data/app/models/ruby_llm/agents/execution/scopes.rb +4 -6
  9. data/app/models/ruby_llm/agents/execution.rb +25 -25
  10. data/app/models/ruby_llm/agents/tenant/budgetable.rb +16 -10
  11. data/app/models/ruby_llm/agents/tenant/resettable.rb +12 -12
  12. data/app/models/ruby_llm/agents/tenant/trackable.rb +7 -7
  13. data/app/services/ruby_llm/agents/agent_registry.rb +6 -6
  14. data/app/views/ruby_llm/agents/executions/_audio_player.html.erb +57 -0
  15. data/app/views/ruby_llm/agents/executions/show.html.erb +8 -0
  16. data/lib/generators/ruby_llm_agents/agent_generator.rb +4 -4
  17. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +6 -6
  18. data/lib/generators/ruby_llm_agents/embedder_generator.rb +4 -4
  19. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -7
  20. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +4 -4
  21. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +6 -6
  22. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +9 -9
  23. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +6 -6
  24. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +4 -4
  25. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +4 -4
  26. data/lib/generators/ruby_llm_agents/install_generator.rb +3 -3
  27. data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +4 -4
  28. data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +2 -2
  29. data/lib/generators/ruby_llm_agents/restructure_generator.rb +13 -13
  30. data/lib/generators/ruby_llm_agents/speaker_generator.rb +6 -6
  31. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +4 -4
  32. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +2 -2
  33. data/lib/ruby_llm/agents/audio/speaker/active_storage_support.rb +87 -0
  34. data/lib/ruby_llm/agents/audio/speaker.rb +50 -31
  35. data/lib/ruby_llm/agents/audio/speech_client.rb +328 -0
  36. data/lib/ruby_llm/agents/audio/speech_pricing.rb +273 -0
  37. data/lib/ruby_llm/agents/audio/transcriber.rb +43 -33
  38. data/lib/ruby_llm/agents/base_agent.rb +14 -14
  39. data/lib/ruby_llm/agents/core/base/callbacks.rb +3 -3
  40. data/lib/ruby_llm/agents/core/configuration.rb +90 -73
  41. data/lib/ruby_llm/agents/core/errors.rb +27 -2
  42. data/lib/ruby_llm/agents/core/instrumentation.rb +64 -66
  43. data/lib/ruby_llm/agents/core/llm_tenant.rb +7 -7
  44. data/lib/ruby_llm/agents/core/version.rb +1 -1
  45. data/lib/ruby_llm/agents/dsl/base.rb +3 -3
  46. data/lib/ruby_llm/agents/dsl/reliability.rb +9 -9
  47. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +1 -1
  48. data/lib/ruby_llm/agents/image/analyzer/execution.rb +4 -4
  49. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +1 -1
  50. data/lib/ruby_llm/agents/image/background_remover/execution.rb +3 -3
  51. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +8 -8
  52. data/lib/ruby_llm/agents/image/editor/execution.rb +1 -1
  53. data/lib/ruby_llm/agents/image/generator/pricing.rb +9 -10
  54. data/lib/ruby_llm/agents/image/generator.rb +6 -6
  55. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +6 -6
  56. data/lib/ruby_llm/agents/image/pipeline/execution.rb +9 -9
  57. data/lib/ruby_llm/agents/image/pipeline.rb +1 -1
  58. data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -1
  59. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +1 -1
  60. data/lib/ruby_llm/agents/image/upscaler/execution.rb +3 -5
  61. data/lib/ruby_llm/agents/image/variator/execution.rb +1 -1
  62. data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +4 -4
  63. data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +4 -4
  64. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +9 -9
  65. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +3 -3
  66. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +1 -1
  67. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +17 -17
  68. data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +1 -0
  69. data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +1 -1
  70. data/lib/ruby_llm/agents/infrastructure/reliability.rb +6 -6
  71. data/lib/ruby_llm/agents/pipeline/builder.rb +11 -11
  72. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +3 -3
  73. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +4 -4
  74. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +83 -22
  75. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +2 -3
  76. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +7 -7
  77. data/lib/ruby_llm/agents/results/background_removal_result.rb +6 -6
  78. data/lib/ruby_llm/agents/results/embedding_result.rb +15 -15
  79. data/lib/ruby_llm/agents/results/image_analysis_result.rb +7 -7
  80. data/lib/ruby_llm/agents/results/image_edit_result.rb +4 -4
  81. data/lib/ruby_llm/agents/results/image_generation_result.rb +5 -5
  82. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +4 -4
  83. data/lib/ruby_llm/agents/results/image_transform_result.rb +4 -4
  84. data/lib/ruby_llm/agents/results/image_upscale_result.rb +5 -5
  85. data/lib/ruby_llm/agents/results/image_variation_result.rb +4 -4
  86. data/lib/ruby_llm/agents/results/speech_result.rb +12 -7
  87. data/lib/ruby_llm/agents/results/transcription_result.rb +1 -1
  88. data/lib/ruby_llm/agents/text/embedder.rb +13 -13
  89. metadata +5 -1
@@ -0,0 +1,273 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+
6
+ module RubyLLM
7
+ module Agents
8
+ module Audio
9
+ # Dynamic pricing resolution for text-to-speech models.
10
+ #
11
+ # Uses the same three-tier strategy as ImageGenerator::Pricing:
12
+ # 1. LiteLLM JSON (primary) - future-proof, auto-updating
13
+ # 2. Configurable pricing table - user overrides via config.tts_model_pricing
14
+ # 3. Hardcoded fallbacks - per-model defaults
15
+ #
16
+ # All prices are per 1,000 characters.
17
+ #
18
+ # @example Get cost for a speech operation
19
+ # SpeechPricing.calculate_cost(provider: :openai, model_id: "tts-1", characters: 5000)
20
+ # # => 0.075
21
+ #
22
+ # @example User-configured pricing
23
+ # RubyLLM::Agents.configure do |c|
24
+ # c.tts_model_pricing = {
25
+ # "eleven_v3" => 0.24,
26
+ # "tts-1" => 0.015
27
+ # }
28
+ # end
29
+ #
30
+ module SpeechPricing
31
+ extend self
32
+
33
+ LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
34
+ DEFAULT_CACHE_TTL = 24 * 60 * 60 # 24 hours
35
+
36
+ # Calculate total cost for a speech operation
37
+ #
38
+ # @param provider [Symbol] :openai or :elevenlabs
39
+ # @param model_id [String] The model identifier
40
+ # @param characters [Integer] Number of characters synthesized
41
+ # @return [Float] Total cost in USD
42
+ def calculate_cost(provider:, model_id:, characters:)
43
+ price_per_1k = cost_per_1k_characters(provider, model_id)
44
+ ((characters / 1000.0) * price_per_1k).round(6)
45
+ end
46
+
47
+ # Get cost per 1,000 characters for a model
48
+ #
49
+ # @param provider [Symbol] Provider identifier
50
+ # @param model_id [String] Model identifier
51
+ # @return [Float] Cost per 1K characters in USD
52
+ def cost_per_1k_characters(provider, model_id)
53
+ if (litellm_price = from_litellm(model_id))
54
+ return litellm_price
55
+ end
56
+
57
+ if (config_price = from_config(model_id))
58
+ return config_price
59
+ end
60
+
61
+ fallback_price(provider, model_id)
62
+ end
63
+
64
+ # Force refresh of cached LiteLLM data
65
+ def refresh!
66
+ @litellm_data = nil
67
+ @litellm_fetched_at = nil
68
+ litellm_data
69
+ end
70
+
71
+ # Expose all known pricing for debugging/dashboard
72
+ def all_pricing
73
+ {
74
+ litellm: litellm_tts_models,
75
+ configured: config.tts_model_pricing || {},
76
+ fallbacks: fallback_pricing_table
77
+ }
78
+ end
79
+
80
+ private
81
+
82
+ # ============================================================
83
+ # Tier 1: LiteLLM
84
+ # ============================================================
85
+
86
+ def from_litellm(model_id)
87
+ data = litellm_data
88
+ return nil unless data
89
+
90
+ model_data = find_litellm_model(data, model_id)
91
+ return nil unless model_data
92
+
93
+ extract_litellm_tts_price(model_data)
94
+ end
95
+
96
+ def find_litellm_model(data, model_id)
97
+ normalized = normalize_model_id(model_id)
98
+
99
+ candidates = [
100
+ model_id,
101
+ normalized,
102
+ "tts/#{model_id}",
103
+ "openai/#{model_id}",
104
+ "elevenlabs/#{model_id}"
105
+ ]
106
+
107
+ candidates.each do |key|
108
+ return data[key] if data[key]
109
+ end
110
+
111
+ data.find do |key, _|
112
+ key.to_s.downcase.include?(normalized.downcase)
113
+ end&.last
114
+ end
115
+
116
+ def extract_litellm_tts_price(model_data)
117
+ if model_data["input_cost_per_character"]
118
+ return model_data["input_cost_per_character"] * 1000
119
+ end
120
+
121
+ if model_data["output_cost_per_character"]
122
+ return model_data["output_cost_per_character"] * 1000
123
+ end
124
+
125
+ if model_data["output_cost_per_audio_token"]
126
+ return model_data["output_cost_per_audio_token"] * 250
127
+ end
128
+
129
+ nil
130
+ end
131
+
132
+ def litellm_data
133
+ return @litellm_data if @litellm_data && !cache_expired?
134
+
135
+ @litellm_data = fetch_litellm_data
136
+ @litellm_fetched_at = Time.now
137
+ @litellm_data
138
+ end
139
+
140
+ def fetch_litellm_data
141
+ if defined?(Rails) && Rails.respond_to?(:cache) && Rails.cache
142
+ Rails.cache.fetch("litellm_tts_pricing_data", expires_in: cache_ttl) do
143
+ fetch_from_url
144
+ end
145
+ else
146
+ fetch_from_url
147
+ end
148
+ rescue => e
149
+ warn "[RubyLLM::Agents] Failed to fetch LiteLLM TTS pricing: #{e.message}"
150
+ {}
151
+ end
152
+
153
+ def fetch_from_url
154
+ uri = URI(config.litellm_pricing_url || LITELLM_PRICING_URL)
155
+ http = Net::HTTP.new(uri.host, uri.port)
156
+ http.use_ssl = uri.scheme == "https"
157
+ http.open_timeout = 5
158
+ http.read_timeout = 10
159
+
160
+ request = Net::HTTP::Get.new(uri)
161
+ response = http.request(request)
162
+
163
+ if response.is_a?(Net::HTTPSuccess)
164
+ JSON.parse(response.body)
165
+ else
166
+ {}
167
+ end
168
+ rescue => e
169
+ warn "[RubyLLM::Agents] HTTP error fetching LiteLLM pricing: #{e.message}"
170
+ {}
171
+ end
172
+
173
+ def cache_expired?
174
+ return true unless @litellm_fetched_at
175
+ Time.now - @litellm_fetched_at > cache_ttl
176
+ end
177
+
178
+ def cache_ttl
179
+ ttl = config.litellm_pricing_cache_ttl
180
+ return DEFAULT_CACHE_TTL unless ttl
181
+ ttl.respond_to?(:to_i) ? ttl.to_i : ttl
182
+ end
183
+
184
+ def litellm_tts_models
185
+ litellm_data.select do |key, value|
186
+ value.is_a?(Hash) && (
187
+ value["input_cost_per_character"] ||
188
+ key.to_s.match?(/tts|speech|eleven/i)
189
+ )
190
+ end
191
+ end
192
+
193
+ # ============================================================
194
+ # Tier 2: User configuration
195
+ # ============================================================
196
+
197
+ def from_config(model_id)
198
+ table = config.tts_model_pricing
199
+ return nil unless table.is_a?(Hash) && !table.empty?
200
+
201
+ normalized = normalize_model_id(model_id)
202
+
203
+ price = table[model_id] || table[normalized] ||
204
+ table[model_id.to_sym] || table[normalized.to_sym]
205
+
206
+ price if price.is_a?(Numeric)
207
+ end
208
+
209
+ # ============================================================
210
+ # Tier 3: Hardcoded fallbacks
211
+ # ============================================================
212
+
213
+ def fallback_price(provider, model_id)
214
+ normalized = normalize_model_id(model_id)
215
+
216
+ case provider
217
+ when :openai
218
+ openai_fallback_price(normalized)
219
+ when :elevenlabs
220
+ elevenlabs_fallback_price(normalized)
221
+ else
222
+ config.default_tts_cost || 0.015
223
+ end
224
+ end
225
+
226
+ def openai_fallback_price(model_id)
227
+ case model_id
228
+ when /tts-1-hd/ then 0.030
229
+ when /tts-1/ then 0.015
230
+ else 0.015
231
+ end
232
+ end
233
+
234
+ def elevenlabs_fallback_price(model_id)
235
+ case model_id
236
+ when /eleven_flash_v2/ then 0.15
237
+ when /eleven_turbo_v2/ then 0.15
238
+ when /eleven_v3/ then 0.30
239
+ when /eleven_multilingual_v2/ then 0.30
240
+ when /eleven_multilingual_v1/ then 0.30
241
+ when /eleven_monolingual_v1/ then 0.30
242
+ else 0.30
243
+ end
244
+ end
245
+
246
+ def fallback_pricing_table
247
+ {
248
+ "tts-1" => 0.015,
249
+ "tts-1-hd" => 0.030,
250
+ "eleven_monolingual_v1" => 0.30,
251
+ "eleven_multilingual_v1" => 0.30,
252
+ "eleven_multilingual_v2" => 0.30,
253
+ "eleven_turbo_v2" => 0.15,
254
+ "eleven_flash_v2" => 0.15,
255
+ "eleven_turbo_v2_5" => 0.15,
256
+ "eleven_flash_v2_5" => 0.15,
257
+ "eleven_v3" => 0.30
258
+ }
259
+ end
260
+
261
+ def normalize_model_id(model_id)
262
+ model_id.to_s.downcase
263
+ .gsub(/[^a-z0-9._-]/, "-").squeeze("-")
264
+ .gsub(/^-|-$/, "")
265
+ end
266
+
267
+ def config
268
+ RubyLLM::Agents.configuration
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
@@ -166,7 +166,7 @@ module RubyLLM
166
166
 
167
167
  def default_transcription_model
168
168
  RubyLLM::Agents.configuration.default_transcription_model
169
- rescue StandardError
169
+ rescue
170
170
  "whisper-1"
171
171
  end
172
172
  end
@@ -318,6 +318,16 @@ module RubyLLM
318
318
  context.output_tokens = 0
319
319
  context.total_cost = calculate_cost(raw_result)
320
320
 
321
+ # Store transcription-specific metadata for execution tracking
322
+ context[:language] = resolved_language if resolved_language
323
+ context[:detected_language] = raw_result[:language] if raw_result[:language]
324
+ context[:audio_duration_seconds] = raw_result[:duration] if raw_result[:duration]
325
+ context[:audio_minutes] = (raw_result[:duration] / 60.0).round(4) if raw_result[:duration]
326
+ context[:output_format] = self.class.output_format.to_s
327
+ context[:timestamp_granularity] = self.class.include_timestamps.to_s
328
+ context[:segment_count] = raw_result[:segments]&.size if raw_result[:segments]
329
+ context[:word_count] = raw_result[:text]&.split(/\s+/)&.size if raw_result[:text]
330
+
321
331
  # Build final result
322
332
  context.output = build_result(
323
333
  raw_result,
@@ -334,22 +344,22 @@ module RubyLLM
334
344
  def agent_cache_key
335
345
  # Generate content hash based on input type
336
346
  content_hash = case @audio
337
- when String
338
- if @audio.start_with?("http://", "https://")
339
- Digest::SHA256.hexdigest(@audio)
340
- elsif File.exist?(@audio)
341
- Digest::SHA256.file(@audio).hexdigest
342
- else
343
- Digest::SHA256.hexdigest(@audio)
344
- end
345
- when File, IO
346
- @audio.rewind if @audio.respond_to?(:rewind)
347
- Digest::SHA256.hexdigest(@audio.read).tap do
348
- @audio.rewind if @audio.respond_to?(:rewind)
349
- end
350
- else
351
- Digest::SHA256.hexdigest(@audio.to_s)
352
- end
347
+ when String
348
+ if @audio.start_with?("http://", "https://")
349
+ Digest::SHA256.hexdigest(@audio)
350
+ elsif File.exist?(@audio)
351
+ Digest::SHA256.file(@audio).hexdigest
352
+ else
353
+ Digest::SHA256.hexdigest(@audio)
354
+ end
355
+ when File, IO
356
+ @audio.rewind if @audio.respond_to?(:rewind)
357
+ Digest::SHA256.hexdigest(@audio.read).tap do
358
+ @audio.rewind if @audio.respond_to?(:rewind)
359
+ end
360
+ else
361
+ Digest::SHA256.hexdigest(@audio.to_s)
362
+ end
353
363
 
354
364
  components = [
355
365
  "ruby_llm_agents",
@@ -389,15 +399,15 @@ module RubyLLM
389
399
  case audio
390
400
  when String
391
401
  if audio.start_with?("http://", "https://")
392
- { source: audio, type: :url }
402
+ {source: audio, type: :url}
393
403
  elsif looks_like_file_path?(audio)
394
- { source: audio, type: :file_path }
404
+ {source: audio, type: :file_path}
395
405
  else
396
406
  # Assume it's binary data
397
- { source: audio, type: :binary, format: format }
407
+ {source: audio, type: :binary, format: format}
398
408
  end
399
409
  when File, IO
400
- { source: audio, type: :file_object }
410
+ {source: audio, type: :file_object}
401
411
  else
402
412
  raise ArgumentError, "audio must be a file path, URL, File object, or binary data"
403
413
  end
@@ -451,7 +461,7 @@ module RubyLLM
451
461
 
452
462
  begin
453
463
  return execute_transcription(audio_input, model)
454
- rescue StandardError => e
464
+ rescue => e
455
465
  last_error = e
456
466
  retries += 1
457
467
 
@@ -498,7 +508,7 @@ module RubyLLM
498
508
  # @param model [String] Model to use
499
509
  # @return [Hash] Options for transcription
500
510
  def build_transcribe_options(model)
501
- options = { model: model }
511
+ options = {model: model}
502
512
 
503
513
  # Add language if specified
504
514
  lang = resolved_language
@@ -618,15 +628,15 @@ module RubyLLM
618
628
  # Estimate based on model and duration
619
629
  model = raw_result[:model].to_s
620
630
  price_per_minute = case model
621
- when /whisper-1/
622
- 0.006
623
- when /gpt-4o-transcribe/
624
- 0.01
625
- when /gpt-4o-mini-transcribe/
626
- 0.005
627
- else
628
- 0.006 # Default to whisper pricing
629
- end
631
+ when /whisper-1/
632
+ 0.006
633
+ when /gpt-4o-transcribe/
634
+ 0.01
635
+ when /gpt-4o-mini-transcribe/
636
+ 0.005
637
+ else
638
+ 0.006 # Default to whisper pricing
639
+ end
630
640
 
631
641
  duration_minutes * price_per_minute
632
642
  end
@@ -657,7 +667,7 @@ module RubyLLM
657
667
  # Calculates exponential backoff delay
658
668
  def calculate_backoff(attempt)
659
669
  config = self.class.reliability_config
660
- base = config&.backoff == :constant ? 1.0 : 0.4
670
+ base = (config&.backoff == :constant) ? 1.0 : 0.4
661
671
  max_delay = 10.0
662
672
 
663
673
  delay = base * (2**(attempt - 1))
@@ -133,7 +133,7 @@ module RubyLLM
133
133
  # @return [void]
134
134
  def param(name, required: false, default: nil, type: nil)
135
135
  @params ||= {}
136
- @params[name] = { required: required, default: default, type: type }
136
+ @params[name] = {required: required, default: default, type: type}
137
137
  define_method(name) do
138
138
  @options[name] || @options[name.to_s] || self.class.params.dig(name, :default)
139
139
  end
@@ -217,7 +217,7 @@ module RubyLLM
217
217
 
218
218
  # Fall back to global configuration default
219
219
  RubyLLM::Agents.configuration.default_thinking
220
- rescue StandardError
220
+ rescue
221
221
  nil
222
222
  end
223
223
 
@@ -227,13 +227,13 @@ module RubyLLM
227
227
 
228
228
  def default_streaming
229
229
  RubyLLM::Agents.configuration.default_streaming
230
- rescue StandardError
230
+ rescue
231
231
  false
232
232
  end
233
233
 
234
234
  def default_temperature
235
235
  RubyLLM::Agents.configuration.default_temperature
236
- rescue StandardError
236
+ rescue
237
237
  0.7
238
238
  end
239
239
  end
@@ -455,7 +455,7 @@ module RubyLLM
455
455
  if tenant_value.is_a?(Hash)
456
456
  tenant_value
457
457
  elsif tenant_value.respond_to?(:llm_tenant_id)
458
- { id: tenant_value.llm_tenant_id, object: tenant_value }
458
+ {id: tenant_value.llm_tenant_id, object: tenant_value}
459
459
  else
460
460
  raise ArgumentError, "tenant must be a Hash or respond to :llm_tenant_id"
461
461
  end
@@ -465,7 +465,7 @@ module RubyLLM
465
465
  #
466
466
  # @return [Array<Class>] Tool classes to use
467
467
  def resolved_tools
468
- if self.class.instance_methods(false).include?(:tools)
468
+ if self.class.method_defined?(:tools, false)
469
469
  tools
470
470
  else
471
471
  self.class.tools
@@ -493,7 +493,7 @@ module RubyLLM
493
493
  prefill = assistant_prompt
494
494
  return nil if prefill.nil? || (prefill.is_a?(String) && prefill.empty?)
495
495
 
496
- { role: :assistant, content: prefill }
496
+ {role: :assistant, content: prefill}
497
497
  end
498
498
 
499
499
  # Returns whether streaming is enabled
@@ -544,7 +544,7 @@ module RubyLLM
544
544
 
545
545
  if config[:type] && has_value && !value.nil? && !value.is_a?(config[:type])
546
546
  raise ArgumentError,
547
- "#{self.class} expected #{config[:type]} for :#{name}, got #{value.class}"
547
+ "#{self.class} expected #{config[:type]} for :#{name}, got #{value.class}"
548
548
  end
549
549
  end
550
550
  end
@@ -594,8 +594,8 @@ module RubyLLM
594
594
  def build_client(context = nil)
595
595
  effective_model = context&.model || model
596
596
  client = RubyLLM.chat
597
- .with_model(effective_model)
598
- .with_temperature(temperature)
597
+ .with_model(effective_model)
598
+ .with_temperature(temperature)
599
599
 
600
600
  client = client.with_instructions(system_prompt) if system_prompt
601
601
  client = client.with_schema(schema) if schema
@@ -735,7 +735,7 @@ module RubyLLM
735
735
  return nil unless defined?(RubyLLM::Models)
736
736
 
737
737
  RubyLLM::Models.find(model_id)
738
- rescue StandardError
738
+ rescue
739
739
  nil
740
740
  end
741
741
 
@@ -789,7 +789,7 @@ module RubyLLM
789
789
  # @return [Hash] Hash with thinking data or empty hash
790
790
  def safe_extract_thinking_data(response)
791
791
  result_thinking_data(response)
792
- rescue StandardError
792
+ rescue
793
793
  {}
794
794
  end
795
795
 
@@ -894,7 +894,7 @@ module RubyLLM
894
894
  content = result.to_s
895
895
  end
896
896
 
897
- { content: content, status: status, error_message: error_message }
897
+ {content: content, status: status, error_message: error_message}
898
898
  end
899
899
 
900
900
  # Truncates tool result if it exceeds the configured max length
@@ -917,7 +917,7 @@ module RubyLLM
917
917
  # @return [Integer] Max length
918
918
  def tool_result_max_length
919
919
  RubyLLM::Agents.configuration.tool_result_max_length || 10_000
920
- rescue StandardError
920
+ rescue
921
921
  10_000
922
922
  end
923
923
 
@@ -49,7 +49,7 @@ module RubyLLM
49
49
  # before_call { |context| context.params[:sanitized] = true }
50
50
  #
51
51
  def before_call(method_name = nil, &block)
52
- @callbacks ||= { before: [], after: [] }
52
+ @callbacks ||= {before: [], after: []}
53
53
  @callbacks[:before] << (block || method_name)
54
54
  end
55
55
 
@@ -71,7 +71,7 @@ module RubyLLM
71
71
  # after_call { |context, response| notify_completion(response) }
72
72
  #
73
73
  def after_call(method_name = nil, &block)
74
- @callbacks ||= { before: [], after: [] }
74
+ @callbacks ||= {before: [], after: []}
75
75
  @callbacks[:after] << (block || method_name)
76
76
  end
77
77
 
@@ -112,7 +112,7 @@ module RubyLLM
112
112
  #
113
113
  # @return [Hash] Hash with :before and :after arrays
114
114
  def callbacks
115
- @callbacks ||= { before: [], after: [] }
115
+ @callbacks ||= {before: [], after: []}
116
116
  end
117
117
  end
118
118