ruby_llm-agents 3.0.0 → 3.2.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 (91) 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 +26 -26
  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/layouts/ruby_llm/agents/application.html.erb +142 -11
  15. data/app/views/ruby_llm/agents/agents/show.html.erb +10 -10
  16. data/app/views/ruby_llm/agents/dashboard/index.html.erb +10 -10
  17. data/app/views/ruby_llm/agents/executions/show.html.erb +13 -0
  18. data/lib/generators/ruby_llm_agents/agent_generator.rb +4 -4
  19. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +6 -6
  20. data/lib/generators/ruby_llm_agents/embedder_generator.rb +4 -4
  21. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -7
  22. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +4 -4
  23. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +6 -6
  24. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +9 -9
  25. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +6 -6
  26. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +4 -4
  27. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +4 -4
  28. data/lib/generators/ruby_llm_agents/install_generator.rb +3 -3
  29. data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +4 -4
  30. data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +2 -2
  31. data/lib/generators/ruby_llm_agents/restructure_generator.rb +13 -13
  32. data/lib/generators/ruby_llm_agents/speaker_generator.rb +6 -6
  33. data/lib/generators/ruby_llm_agents/templates/add_assistant_prompt_migration.rb.tt +9 -0
  34. data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +2 -1
  35. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +4 -4
  36. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +22 -3
  37. data/lib/ruby_llm/agents/audio/speaker.rb +40 -31
  38. data/lib/ruby_llm/agents/audio/speech_client.rb +328 -0
  39. data/lib/ruby_llm/agents/audio/speech_pricing.rb +273 -0
  40. data/lib/ruby_llm/agents/audio/transcriber.rb +33 -33
  41. data/lib/ruby_llm/agents/base_agent.rb +16 -15
  42. data/lib/ruby_llm/agents/core/base/callbacks.rb +3 -3
  43. data/lib/ruby_llm/agents/core/configuration.rb +86 -73
  44. data/lib/ruby_llm/agents/core/errors.rb +27 -2
  45. data/lib/ruby_llm/agents/core/instrumentation.rb +101 -65
  46. data/lib/ruby_llm/agents/core/llm_tenant.rb +7 -7
  47. data/lib/ruby_llm/agents/core/version.rb +1 -1
  48. data/lib/ruby_llm/agents/dsl/base.rb +3 -3
  49. data/lib/ruby_llm/agents/dsl/reliability.rb +9 -9
  50. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +1 -1
  51. data/lib/ruby_llm/agents/image/analyzer/execution.rb +4 -4
  52. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +1 -1
  53. data/lib/ruby_llm/agents/image/background_remover/execution.rb +3 -3
  54. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +8 -8
  55. data/lib/ruby_llm/agents/image/editor/execution.rb +1 -1
  56. data/lib/ruby_llm/agents/image/generator/pricing.rb +9 -10
  57. data/lib/ruby_llm/agents/image/generator.rb +6 -6
  58. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +6 -6
  59. data/lib/ruby_llm/agents/image/pipeline/execution.rb +9 -9
  60. data/lib/ruby_llm/agents/image/pipeline.rb +1 -1
  61. data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -1
  62. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +1 -1
  63. data/lib/ruby_llm/agents/image/upscaler/execution.rb +3 -5
  64. data/lib/ruby_llm/agents/image/variator/execution.rb +1 -1
  65. data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +4 -4
  66. data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +4 -4
  67. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +9 -9
  68. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +3 -3
  69. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +1 -1
  70. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +17 -17
  71. data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +1 -0
  72. data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +1 -1
  73. data/lib/ruby_llm/agents/infrastructure/reliability.rb +6 -6
  74. data/lib/ruby_llm/agents/pipeline/builder.rb +11 -11
  75. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +3 -3
  76. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +4 -4
  77. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +62 -21
  78. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +2 -3
  79. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +82 -4
  80. data/lib/ruby_llm/agents/results/background_removal_result.rb +6 -6
  81. data/lib/ruby_llm/agents/results/embedding_result.rb +15 -15
  82. data/lib/ruby_llm/agents/results/image_analysis_result.rb +7 -7
  83. data/lib/ruby_llm/agents/results/image_edit_result.rb +4 -4
  84. data/lib/ruby_llm/agents/results/image_generation_result.rb +5 -5
  85. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +4 -4
  86. data/lib/ruby_llm/agents/results/image_transform_result.rb +4 -4
  87. data/lib/ruby_llm/agents/results/image_upscale_result.rb +5 -5
  88. data/lib/ruby_llm/agents/results/image_variation_result.rb +4 -4
  89. data/lib/ruby_llm/agents/results/transcription_result.rb +1 -1
  90. data/lib/ruby_llm/agents/text/embedder.rb +13 -13
  91. metadata +4 -1
@@ -63,48 +63,46 @@ module RubyLLM
63
63
  # Use catch to intercept successful early returns from the block
64
64
  # The block uses `throw :execution_success, result` instead of `return`
65
65
  result = catch(:execution_success) do
66
+ yield(attempt_tracker)
67
+ # If we reach here normally (no throw), the block completed without success
68
+ # This happens when AllModelsExhaustedError is raised
69
+ nil
70
+ rescue Timeout::Error, Reliability::TotalTimeoutError => e
71
+ raised_exception = e
66
72
  begin
67
- yield(attempt_tracker)
68
- # If we reach here normally (no throw), the block completed without success
69
- # This happens when AllModelsExhaustedError is raised
70
- nil
71
- rescue Timeout::Error, Reliability::TotalTimeoutError => e
72
- raised_exception = e
73
- begin
74
- complete_execution_with_attempts(
75
- execution,
76
- attempt_tracker: attempt_tracker,
77
- completed_at: Time.current,
78
- status: "timeout",
79
- error: e
80
- )
81
- @status_update_completed = true
82
- rescue StandardError => completion_err
83
- completion_error = completion_err
84
- end
85
- raise
86
- rescue StandardError => e
87
- raised_exception = e
88
- begin
89
- complete_execution_with_attempts(
90
- execution,
91
- attempt_tracker: attempt_tracker,
92
- completed_at: Time.current,
93
- status: "error",
94
- error: e
95
- )
96
- @status_update_completed = true
97
- rescue StandardError => completion_err
98
- completion_error = completion_err
99
- end
100
- raise
101
- ensure
102
- # Only run emergency fallback if we haven't completed AND we're not in success path
103
- # The success path completion happens AFTER the catch block
104
- unless @status_update_completed || !$!
105
- actual_error = completion_error || raised_exception || $!
106
- mark_execution_failed!(execution, error: actual_error)
107
- end
73
+ complete_execution_with_attempts(
74
+ execution,
75
+ attempt_tracker: attempt_tracker,
76
+ completed_at: Time.current,
77
+ status: "timeout",
78
+ error: e
79
+ )
80
+ @status_update_completed = true
81
+ rescue => completion_err
82
+ completion_error = completion_err
83
+ end
84
+ raise
85
+ rescue => e
86
+ raised_exception = e
87
+ begin
88
+ complete_execution_with_attempts(
89
+ execution,
90
+ attempt_tracker: attempt_tracker,
91
+ completed_at: Time.current,
92
+ status: "error",
93
+ error: e
94
+ )
95
+ @status_update_completed = true
96
+ rescue => completion_err
97
+ completion_error = completion_err
98
+ end
99
+ raise
100
+ ensure
101
+ # Only run emergency fallback if we haven't completed AND we're not in success path
102
+ # The success path completion happens AFTER the catch block
103
+ unless @status_update_completed || !$!
104
+ actual_error = completion_error || raised_exception || $!
105
+ mark_execution_failed!(execution, error: actual_error)
108
106
  end
109
107
  end
110
108
 
@@ -119,7 +117,7 @@ module RubyLLM
119
117
  status: "success"
120
118
  )
121
119
  @status_update_completed = true
122
- rescue StandardError => e
120
+ rescue => e
123
121
  Rails.logger.error("[RubyLLM::Agents] Failed to complete successful execution: #{e.class}: #{e.message}")
124
122
  mark_execution_failed!(execution, error: e)
125
123
  end
@@ -165,7 +163,7 @@ module RubyLLM
165
163
  response: @last_response
166
164
  )
167
165
  @status_update_completed = true
168
- rescue StandardError => e
166
+ rescue => e
169
167
  completion_error = e
170
168
  # Don't re-raise - let ensure block handle via mark_execution_failed!
171
169
  end
@@ -181,11 +179,11 @@ module RubyLLM
181
179
  error: e
182
180
  )
183
181
  @status_update_completed = true
184
- rescue StandardError => completion_err
182
+ rescue => completion_err
185
183
  completion_error = completion_err
186
184
  end
187
185
  raise
188
- rescue StandardError => e
186
+ rescue => e
189
187
  raised_exception = e
190
188
  begin
191
189
  complete_execution(
@@ -195,7 +193,7 @@ module RubyLLM
195
193
  error: e
196
194
  )
197
195
  @status_update_completed = true
198
- rescue StandardError => completion_err
196
+ rescue => completion_err
199
197
  completion_error = completion_err
200
198
  end
201
199
  raise
@@ -258,7 +256,7 @@ module RubyLLM
258
256
  # Add fallback chain tracking (count only on execution, chain stored in detail)
259
257
  if fallback_chain.any?
260
258
  execution_data[:attempts_count] = 0
261
- @_pending_detail_data = { fallback_chain: fallback_chain, attempts: [] }
259
+ @_pending_detail_data = {fallback_chain: fallback_chain, attempts: []}
262
260
  end
263
261
 
264
262
  # Add tenant_id if multi-tenancy is enabled
@@ -275,6 +273,9 @@ module RubyLLM
275
273
  system_prompt: config.persist_prompts ? stored_system_prompt : nil,
276
274
  user_prompt: config.persist_prompts ? stored_user_prompt : nil
277
275
  }
276
+ if config.persist_prompts && assistant_prompt_column_exists?
277
+ detail_data[:assistant_prompt] = stored_assistant_prompt
278
+ end
278
279
  detail_data.merge!(@_pending_detail_data) if @_pending_detail_data
279
280
  @_pending_detail_data = nil
280
281
 
@@ -282,7 +283,7 @@ module RubyLLM
282
283
  execution.create_detail!(detail_data) if has_data
283
284
 
284
285
  execution
285
- rescue StandardError => e
286
+ rescue => e
286
287
  # Log error but don't fail the agent execution itself
287
288
  Rails.logger.error("[RubyLLM::Agents] Failed to create execution record: #{e.message}")
288
289
  nil
@@ -353,7 +354,7 @@ module RubyLLM
353
354
  begin
354
355
  execution.calculate_costs!
355
356
  execution.save!
356
- rescue StandardError => cost_error
357
+ rescue => cost_error
357
358
  Rails.logger.warn("[RubyLLM::Agents] Cost calculation failed: #{cost_error.message}")
358
359
  end
359
360
  end
@@ -361,12 +362,12 @@ module RubyLLM
361
362
  # Record token usage for budget tracking
362
363
  record_token_usage(execution)
363
364
  rescue ActiveRecord::RecordInvalid => e
364
- Rails.logger.error("[RubyLLM::Agents] Validation failed for execution #{execution&.id}: #{e.record.errors.full_messages.join(', ')}")
365
+ Rails.logger.error("[RubyLLM::Agents] Validation failed for execution #{execution&.id}: #{e.record.errors.full_messages.join(", ")}")
365
366
  if Rails.env.development? || Rails.env.test?
366
367
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
367
368
  end
368
369
  raise
369
- rescue StandardError => e
370
+ rescue => e
370
371
  Rails.logger.error("[RubyLLM::Agents] Failed to update execution record #{execution&.id}: #{e.class}: #{e.message}")
371
372
  if Rails.env.development? || Rails.env.test?
372
373
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
@@ -462,7 +463,7 @@ module RubyLLM
462
463
  begin
463
464
  execution.aggregate_attempt_costs!
464
465
  execution.save!
465
- rescue StandardError => cost_error
466
+ rescue => cost_error
466
467
  Rails.logger.warn("[RubyLLM::Agents] Cost calculation failed: #{cost_error.message}")
467
468
  end
468
469
  end
@@ -470,12 +471,12 @@ module RubyLLM
470
471
  # Record token usage for budget tracking
471
472
  record_token_usage(execution)
472
473
  rescue ActiveRecord::RecordInvalid => e
473
- Rails.logger.error("[RubyLLM::Agents] Validation failed for execution #{execution&.id}: #{e.record.errors.full_messages.join(', ')}")
474
+ Rails.logger.error("[RubyLLM::Agents] Validation failed for execution #{execution&.id}: #{e.record.errors.full_messages.join(", ")}")
474
475
  if Rails.env.development? || Rails.env.test?
475
476
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
476
477
  end
477
478
  raise
478
- rescue StandardError => e
479
+ rescue => e
479
480
  Rails.logger.error("[RubyLLM::Agents] Failed to update execution record #{execution&.id}: #{e.class}: #{e.message}")
480
481
  if Rails.env.development? || Rails.env.test?
481
482
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
@@ -528,7 +529,9 @@ module RubyLLM
528
529
  user_prompt: safe_user_prompt,
529
530
  messages_summary: config.persist_messages_summary ? messages_summary : {},
530
531
  error_message: error&.message
531
- }.merge(detail_fields || {})
532
+ }
533
+ detail_data[:assistant_prompt] = safe_assistant_prompt if assistant_prompt_column_exists?
534
+ detail_data.merge!(detail_fields || {})
532
535
 
533
536
  execution_data[:_detail_data] = detail_data
534
537
 
@@ -619,7 +622,7 @@ module RubyLLM
619
622
  # @return [String, nil] The system prompt or nil if unavailable
620
623
  def safe_system_prompt
621
624
  respond_to?(:system_prompt) ? system_prompt.to_s : nil
622
- rescue StandardError => e
625
+ rescue => e
623
626
  Rails.logger.warn("[RubyLLM::Agents] Could not capture system_prompt: #{e.message}")
624
627
  nil
625
628
  end
@@ -629,11 +632,28 @@ module RubyLLM
629
632
  # @return [String, nil] The user prompt or nil if unavailable
630
633
  def safe_user_prompt
631
634
  respond_to?(:user_prompt) ? user_prompt.to_s : nil
632
- rescue StandardError => e
635
+ rescue => e
633
636
  Rails.logger.warn("[RubyLLM::Agents] Could not capture user_prompt: #{e.message}")
634
637
  nil
635
638
  end
636
639
 
640
+ # Safely captures assistant prompt, handling errors gracefully
641
+ #
642
+ # @return [String, nil] The assistant prompt or nil if unavailable
643
+ def safe_assistant_prompt
644
+ respond_to?(:assistant_prompt) ? assistant_prompt&.to_s : nil
645
+ rescue => e
646
+ Rails.logger.warn("[RubyLLM::Agents] Could not capture assistant_prompt: #{e.message}")
647
+ nil
648
+ end
649
+
650
+ # Returns the assistant prompt for storage
651
+ #
652
+ # @return [String, nil] The assistant prompt
653
+ def stored_assistant_prompt
654
+ safe_assistant_prompt
655
+ end
656
+
637
657
  # Safely extracts a value from response object
638
658
  #
639
659
  # @param response [Object] The response object
@@ -643,7 +663,7 @@ module RubyLLM
643
663
  def safe_response_value(response, method, default = nil)
644
664
  return default unless response.respond_to?(method)
645
665
  response.public_send(method)
646
- rescue StandardError
666
+ rescue
647
667
  default
648
668
  end
649
669
 
@@ -680,7 +700,7 @@ module RubyLLM
680
700
  # @return [String, nil] Normalized finish reason
681
701
  def safe_extract_finish_reason(response)
682
702
  reason = safe_response_value(response, :finish_reason) ||
683
- safe_response_value(response, :stop_reason)
703
+ safe_response_value(response, :stop_reason)
684
704
  return nil unless reason
685
705
 
686
706
  # Normalize to standard values
@@ -828,7 +848,7 @@ module RubyLLM
828
848
  if tool_call.respond_to?(:to_h)
829
849
  tool_call.to_h
830
850
  else
831
- { id: id, name: tool_call[:name], arguments: tool_call[:arguments] }
851
+ {id: id, name: tool_call[:name], arguments: tool_call[:arguments]}
832
852
  end
833
853
  end
834
854
  end
@@ -897,7 +917,7 @@ module RubyLLM
897
917
  }
898
918
  execution.create_detail!(detail_data) if detail_data.values.any?(&:present?)
899
919
  end
900
- rescue StandardError => e
920
+ rescue => e
901
921
  Rails.logger.error("[RubyLLM::Agents] Failed to record cache hit execution: #{e.message}")
902
922
  end
903
923
 
@@ -918,11 +938,27 @@ module RubyLLM
918
938
  tenant_id: tenant_id,
919
939
  tenant_config: tenant_config
920
940
  )
921
- rescue StandardError => e
941
+ rescue => e
922
942
  Rails.logger.warn("[RubyLLM::Agents] Failed to record token usage: #{e.message}")
923
943
  end
924
944
  end
925
945
 
946
+ # Checks if the assistant_prompt column exists on execution_details
947
+ #
948
+ # Memoized to avoid repeated schema queries. Returns false for older installs
949
+ # that haven't run the migration yet.
950
+ #
951
+ # @return [Boolean] true if the column exists
952
+ def assistant_prompt_column_exists?
953
+ return @_assistant_prompt_column_exists if defined?(@_assistant_prompt_column_exists)
954
+
955
+ @_assistant_prompt_column_exists = begin
956
+ RubyLLM::Agents::ExecutionDetail.column_names.include?("assistant_prompt")
957
+ rescue
958
+ false
959
+ end
960
+ end
961
+
926
962
  # Emergency fallback to mark execution as failed
927
963
  #
928
964
  # Uses update_all to bypass ActiveRecord callbacks and validations,
@@ -963,16 +999,16 @@ module RubyLLM
963
999
 
964
1000
  # Store error_message in detail table (best-effort)
965
1001
  begin
966
- detail_attrs = { error_message: error_message.to_s.truncate(65535) }
1002
+ detail_attrs = {error_message: error_message.to_s.truncate(65535)}
967
1003
  if execution.detail
968
1004
  execution.detail.update_columns(detail_attrs)
969
1005
  else
970
1006
  RubyLLM::Agents::ExecutionDetail.create!(detail_attrs.merge(execution_id: execution.id))
971
1007
  end
972
- rescue StandardError
1008
+ rescue
973
1009
  # Non-critical — error_class on execution is sufficient for filtering
974
1010
  end
975
- rescue StandardError => e
1011
+ rescue => e
976
1012
  Rails.logger.error("[RubyLLM::Agents] CRITICAL: Failed emergency status update for execution #{execution&.id}: #{e.message}")
977
1013
  end
978
1014
  end
@@ -70,9 +70,9 @@ module RubyLLM
70
70
  included do
71
71
  # Link to gem's Tenant model via polymorphic association
72
72
  has_one :llm_tenant_record,
73
- class_name: "RubyLLM::Agents::Tenant",
74
- as: :tenant_record,
75
- dependent: :destroy
73
+ class_name: "RubyLLM::Agents::Tenant",
74
+ as: :tenant_record,
75
+ dependent: :destroy
76
76
 
77
77
  # Backward compatible alias (llm_budget points to same Tenant record)
78
78
  # @deprecated Use llm_tenant_record instead
@@ -106,10 +106,10 @@ module RubyLLM
106
106
 
107
107
  # Executions tracked for this tenant via tenant_id string column
108
108
  has_many :llm_executions,
109
- class_name: "RubyLLM::Agents::Execution",
110
- foreign_key: :tenant_id,
111
- primary_key: id,
112
- dependent: :nullify
109
+ class_name: "RubyLLM::Agents::Execution",
110
+ foreign_key: :tenant_id,
111
+ primary_key: id,
112
+ dependent: :nullify
113
113
 
114
114
  # Auto-create tenant record callback
115
115
  after_create :create_default_llm_tenant if llm_tenant_options[:budget]
@@ -4,6 +4,6 @@ module RubyLLM
4
4
  module Agents
5
5
  # Current version of the RubyLLM::Agents gem
6
6
  # @return [String] Semantic version string
7
- VERSION = "3.0.0"
7
+ VERSION = "3.2.0"
8
8
  end
9
9
  end
@@ -56,7 +56,7 @@ module RubyLLM
56
56
  #
57
57
  module Base
58
58
  # Regex pattern to extract {placeholder} parameters from prompt strings
59
- PLACEHOLDER_PATTERN = /\{(\w+)\}/.freeze
59
+ PLACEHOLDER_PATTERN = /\{(\w+)\}/
60
60
 
61
61
  # @!group Configuration DSL
62
62
 
@@ -306,7 +306,7 @@ module RubyLLM
306
306
  # @return [String] The default model
307
307
  def default_model
308
308
  RubyLLM::Agents.configuration.default_model
309
- rescue StandardError
309
+ rescue
310
310
  "gpt-4o"
311
311
  end
312
312
 
@@ -315,7 +315,7 @@ module RubyLLM
315
315
  # @return [Integer] The default timeout
316
316
  def default_timeout
317
317
  RubyLLM::Agents.configuration.default_timeout
318
- rescue StandardError
318
+ rescue
319
319
  120
320
320
  end
321
321
  end
@@ -168,7 +168,7 @@ module RubyLLM
168
168
  def fallback_provider(provider = nil, **options)
169
169
  if provider
170
170
  @fallback_providers ||= []
171
- @fallback_providers << { provider: provider, **options }
171
+ @fallback_providers << {provider: provider, **options}
172
172
  end
173
173
  @fallback_providers || inherited_fallback_providers || []
174
174
  end
@@ -201,7 +201,7 @@ module RubyLLM
201
201
  # circuit_breaker errors: 10, within: 60, cooldown: 300
202
202
  def circuit_breaker(errors: nil, within: nil, cooldown: nil)
203
203
  if errors || within || cooldown
204
- @circuit_breaker_config ||= { errors: 10, within: 60, cooldown: 300 }
204
+ @circuit_breaker_config ||= {errors: 10, within: 60, cooldown: 300}
205
205
  @circuit_breaker_config[:errors] = errors if errors
206
206
  @circuit_breaker_config[:within] = within if within
207
207
  @circuit_breaker_config[:cooldown] = cooldown if cooldown
@@ -297,8 +297,8 @@ module RubyLLM
297
297
  # Inner builder class for block-style configuration
298
298
  class ReliabilityBuilder
299
299
  attr_reader :retries_config, :fallback_models_list, :total_timeout_value,
300
- :circuit_breaker_config, :retryable_patterns_list, :fallback_providers_list,
301
- :non_fallback_errors_list
300
+ :circuit_breaker_config, :retryable_patterns_list, :fallback_providers_list,
301
+ :non_fallback_errors_list
302
302
 
303
303
  def initialize
304
304
  @retries_config = nil
@@ -332,7 +332,7 @@ module RubyLLM
332
332
  # fallback_provider :openai, voice: "nova"
333
333
  # fallback_provider :elevenlabs, voice: "Rachel", model: "eleven_multilingual_v2"
334
334
  def fallback_provider(provider, **options)
335
- @fallback_providers_list << { provider: provider, **options }
335
+ @fallback_providers_list << {provider: provider, **options}
336
336
  end
337
337
 
338
338
  def total_timeout(seconds)
@@ -366,8 +366,8 @@ module RubyLLM
366
366
  #
367
367
  class OnFailureBuilder
368
368
  attr_reader :retries_config, :fallback_models_list, :total_timeout_value,
369
- :circuit_breaker_config, :retryable_patterns_list, :fallback_providers_list,
370
- :non_fallback_errors_list
369
+ :circuit_breaker_config, :retryable_patterns_list, :fallback_providers_list,
370
+ :non_fallback_errors_list
371
371
 
372
372
  def initialize
373
373
  @retries_config = nil
@@ -423,7 +423,7 @@ module RubyLLM
423
423
  # @param options [Hash] Provider-specific options
424
424
  #
425
425
  def fallback_provider(provider, **options)
426
- @fallback_providers_list << { provider: provider, **options }
426
+ @fallback_providers_list << {provider: provider, **options}
427
427
  end
428
428
 
429
429
  # Configure timeout for all retry/fallback attempts
@@ -440,7 +440,7 @@ module RubyLLM
440
440
  end
441
441
 
442
442
  # Also support total_timeout for compatibility
443
- alias total_timeout timeout
443
+ alias_method :total_timeout, :timeout
444
444
 
445
445
  # Configure circuit breaker
446
446
  #
@@ -31,7 +31,7 @@ module RubyLLM
31
31
  def analysis_type(value = nil)
32
32
  if value
33
33
  unless VALID_ANALYSIS_TYPES.include?(value)
34
- raise ArgumentError, "Analysis type must be one of: #{VALID_ANALYSIS_TYPES.join(', ')}"
34
+ raise ArgumentError, "Analysis type must be one of: #{VALID_ANALYSIS_TYPES.join(", ")}"
35
35
  end
36
36
  @analysis_type = value
37
37
  else
@@ -46,7 +46,7 @@ module RubyLLM
46
46
  record_execution(result) if execution_tracking_enabled?
47
47
 
48
48
  result
49
- rescue StandardError => e
49
+ rescue => e
50
50
  record_failed_execution(e, started_at) if execution_tracking_enabled?
51
51
  build_error_result(e, started_at)
52
52
  end
@@ -138,8 +138,8 @@ module RubyLLM
138
138
  caption: "Brief caption string",
139
139
  description: "Detailed description string (if applicable)",
140
140
  tags: ["array", "of", "tag", "strings"],
141
- objects: [{ name: "object name", location: "position", confidence: "high/medium/low" }],
142
- colors: [{ hex: "#RRGGBB", name: "color name", percentage: 25 }],
141
+ objects: [{name: "object name", location: "position", confidence: "high/medium/low"}],
142
+ colors: [{hex: "#RRGGBB", name: "color name", percentage: 25}],
143
143
  text: "Extracted text if any"
144
144
  }
145
145
 
@@ -154,7 +154,7 @@ module RubyLLM
154
154
 
155
155
  # Use RubyLLM chat with vision
156
156
  chat = RubyLLM.chat(model: model)
157
- chat.ask(prompt, with: { image: image_content })
157
+ chat.ask(prompt, with: {image: image_content})
158
158
  end
159
159
 
160
160
  def prepare_image_content
@@ -30,7 +30,7 @@ module RubyLLM
30
30
  def output_format(value = nil)
31
31
  if value
32
32
  unless VALID_OUTPUT_FORMATS.include?(value)
33
- raise ArgumentError, "Output format must be one of: #{VALID_OUTPUT_FORMATS.join(', ')}"
33
+ raise ArgumentError, "Output format must be one of: #{VALID_OUTPUT_FORMATS.join(", ")}"
34
34
  end
35
35
  @output_format = value
36
36
  else
@@ -46,7 +46,7 @@ module RubyLLM
46
46
  record_execution(result) if execution_tracking_enabled?
47
47
 
48
48
  result
49
- rescue StandardError => e
49
+ rescue => e
50
50
  record_failed_execution(e, started_at) if execution_tracking_enabled?
51
51
  build_error_result(e, started_at)
52
52
  end
@@ -112,13 +112,13 @@ module RubyLLM
112
112
  mode: "segmentation_mask",
113
113
  **build_removal_options
114
114
  )
115
- rescue StandardError
115
+ rescue
116
116
  # Mask generation failed, continue without it
117
117
  mask = nil
118
118
  end
119
119
  end
120
120
 
121
- { foreground: foreground, mask: mask }
121
+ {foreground: foreground, mask: mask}
122
122
  end
123
123
 
124
124
  def build_removal_options
@@ -20,11 +20,11 @@ module RubyLLM
20
20
  return unless tenant
21
21
 
22
22
  @tenant_id = case tenant
23
- when Hash then tenant[:id]
24
- when Integer, String then tenant
25
- else
26
- tenant.try(:llm_tenant_id) || tenant.try(:id)
27
- end
23
+ when Hash then tenant[:id]
24
+ when Integer, String then tenant
25
+ else
26
+ tenant.try(:llm_tenant_id) || tenant.try(:id)
27
+ end
28
28
  end
29
29
 
30
30
  # Check budget before execution
@@ -87,7 +87,7 @@ module RubyLLM
87
87
  else
88
88
  RubyLLM::Agents::Execution.create!(execution_data)
89
89
  end
90
- rescue StandardError => e
90
+ rescue => e
91
91
  Rails.logger.error("[RubyLLM::Agents] Failed to record #{execution_type} execution: #{e.message}") if defined?(Rails)
92
92
  end
93
93
 
@@ -101,7 +101,7 @@ module RubyLLM
101
101
  else
102
102
  RubyLLM::Agents::Execution.create!(execution_data)
103
103
  end
104
- rescue StandardError => e
104
+ rescue => e
105
105
  Rails.logger.error("[RubyLLM::Agents] Failed to record failed #{execution_type} execution: #{e.message}") if defined?(Rails)
106
106
  end
107
107
 
@@ -142,7 +142,7 @@ module RubyLLM
142
142
  end
143
143
 
144
144
  def build_metadata(result)
145
- { count: result.count }
145
+ {count: result.count}
146
146
  end
147
147
 
148
148
  def budget_tracking_enabled?
@@ -45,7 +45,7 @@ module RubyLLM
45
45
  record_execution(result) if execution_tracking_enabled?
46
46
 
47
47
  result
48
- rescue StandardError => e
48
+ rescue => e
49
49
  record_failed_execution(e, started_at) if execution_tracking_enabled?
50
50
  build_error_result(e, started_at)
51
51
  end
@@ -158,7 +158,7 @@ module RubyLLM
158
158
  else
159
159
  fetch_from_url
160
160
  end
161
- rescue StandardError => e
161
+ rescue => e
162
162
  warn "[RubyLLM::Agents] Failed to fetch LiteLLM pricing: #{e.message}"
163
163
  {}
164
164
  end
@@ -178,7 +178,7 @@ module RubyLLM
178
178
  else
179
179
  {}
180
180
  end
181
- rescue StandardError => e
181
+ rescue => e
182
182
  warn "[RubyLLM::Agents] HTTP error fetching LiteLLM pricing: #{e.message}"
183
183
  {}
184
184
  end
@@ -314,9 +314,9 @@ module RubyLLM
314
314
 
315
315
  def fallback_pricing_table
316
316
  {
317
- "gpt-image-1" => { standard: 0.04, hd: 0.08, large_hd: 0.12 },
318
- "dall-e-3" => { standard: 0.04, hd: 0.08, large_hd: 0.12 },
319
- "dall-e-2" => { "1024x1024" => 0.02, "512x512" => 0.018, "256x256" => 0.016 },
317
+ "gpt-image-1" => {standard: 0.04, hd: 0.08, large_hd: 0.12},
318
+ "dall-e-3" => {standard: 0.04, hd: 0.08, large_hd: 0.12},
319
+ "dall-e-2" => {"1024x1024" => 0.02, "512x512" => 0.018, "256x256" => 0.016},
320
320
  "flux-pro" => 0.05,
321
321
  "flux-dev" => 0.025,
322
322
  "flux-schnell" => 0.003,
@@ -332,16 +332,15 @@ module RubyLLM
332
332
  width, height = size.to_s.split("x").map(&:to_i)
333
333
  return nil if width.zero? || height.zero?
334
334
  width * height
335
- rescue StandardError
335
+ rescue
336
336
  nil
337
337
  end
338
338
 
339
339
  def normalize_model_id(model_id)
340
340
  model_id.to_s
341
- .downcase
342
- .gsub(/[^a-z0-9.-]/, "-")
343
- .gsub(/-+/, "-")
344
- .gsub(/^-|-$/, "")
341
+ .downcase
342
+ .gsub(/[^a-z0-9.-]/, "-").squeeze("-")
343
+ .gsub(/^-|-$/, "")
345
344
  end
346
345
 
347
346
  def config