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
@@ -168,25 +168,25 @@ module RubyLLM
168
168
 
169
169
  def default_image_model
170
170
  RubyLLM::Agents.configuration.default_image_model
171
- rescue StandardError
171
+ rescue
172
172
  "dall-e-3"
173
173
  end
174
174
 
175
175
  def default_image_size
176
176
  RubyLLM::Agents.configuration.default_image_size
177
- rescue StandardError
177
+ rescue
178
178
  "1024x1024"
179
179
  end
180
180
 
181
181
  def default_image_quality
182
182
  RubyLLM::Agents.configuration.default_image_quality
183
- rescue StandardError
183
+ rescue
184
184
  "standard"
185
185
  end
186
186
 
187
187
  def default_image_style
188
188
  RubyLLM::Agents.configuration.default_image_style
189
- rescue StandardError
189
+ rescue
190
190
  "vivid"
191
191
  end
192
192
  end
@@ -241,7 +241,7 @@ module RubyLLM
241
241
  images = generate_images
242
242
 
243
243
  execution_completed_at = Time.current
244
- duration_ms = ((execution_completed_at - execution_started_at) * 1000).to_i
244
+ ((execution_completed_at - execution_started_at) * 1000).to_i
245
245
 
246
246
  # Build result
247
247
  result = build_result(
@@ -257,7 +257,7 @@ module RubyLLM
257
257
  context.total_cost = result.total_cost
258
258
 
259
259
  context.output = result
260
- rescue StandardError => e
260
+ rescue => e
261
261
  execution_completed_at = Time.current
262
262
  context.output = build_error_result(
263
263
  e,
@@ -76,7 +76,7 @@ module RubyLLM
76
76
  # before_pipeline { |ctx| ctx[:started_at] = Time.current }
77
77
  #
78
78
  def before_pipeline(method_name = nil, &block)
79
- @callbacks ||= { before: [], after: [] }
79
+ @callbacks ||= {before: [], after: []}
80
80
  @callbacks[:before] << (block || method_name)
81
81
  end
82
82
 
@@ -91,7 +91,7 @@ module RubyLLM
91
91
  # after_pipeline { |result| notify_completion(result) }
92
92
  #
93
93
  def after_pipeline(method_name = nil, &block)
94
- @callbacks ||= { before: [], after: [] }
94
+ @callbacks ||= {before: [], after: []}
95
95
  @callbacks[:after] << (block || method_name)
96
96
  end
97
97
 
@@ -99,7 +99,7 @@ module RubyLLM
99
99
  #
100
100
  # @return [Hash] Hash with :before and :after arrays
101
101
  def callbacks
102
- @callbacks ||= { before: [], after: [] }
102
+ @callbacks ||= {before: [], after: []}
103
103
  end
104
104
 
105
105
  # Set or get the description
@@ -148,7 +148,7 @@ module RubyLLM
148
148
  end
149
149
  end
150
150
 
151
- alias stop_on_error? stop_on_error
151
+ alias_method :stop_on_error?, :stop_on_error
152
152
 
153
153
  private
154
154
 
@@ -163,11 +163,11 @@ module RubyLLM
163
163
  step_keys = config.keys & valid_keys
164
164
 
165
165
  if step_keys.empty?
166
- raise ArgumentError, "Step :#{name} must specify one of: #{valid_keys.join(', ')}"
166
+ raise ArgumentError, "Step :#{name} must specify one of: #{valid_keys.join(", ")}"
167
167
  end
168
168
 
169
169
  if step_keys.size > 1
170
- raise ArgumentError, "Step :#{name} can only specify one step type, got: #{step_keys.join(', ')}"
170
+ raise ArgumentError, "Step :#{name} can only specify one step type, got: #{step_keys.join(", ")}"
171
171
  end
172
172
 
173
173
  # Validate the class responds to call
@@ -37,7 +37,7 @@ module RubyLLM
37
37
  next unless should_run_step?(step_def)
38
38
 
39
39
  result = execute_step(step_def, current_image)
40
- @step_results << { name: step_def[:name], type: step_def[:type], result: result }
40
+ @step_results << {name: step_def[:name], type: step_def[:type], result: result}
41
41
 
42
42
  # Update context with result
43
43
  @context[step_def[:name]] = result
@@ -63,7 +63,7 @@ module RubyLLM
63
63
  record_execution(result) if execution_tracking_enabled?
64
64
 
65
65
  result
66
- rescue StandardError => e
66
+ rescue => e
67
67
  record_failed_execution(e) if execution_tracking_enabled?
68
68
  build_error_result(e)
69
69
  end
@@ -218,11 +218,11 @@ module RubyLLM
218
218
  return unless tenant
219
219
 
220
220
  @tenant_id = case tenant
221
- when Hash then tenant[:id]
222
- when Integer, String then tenant
223
- else
224
- tenant.try(:llm_tenant_id) || tenant.try(:id)
225
- end
221
+ when Hash then tenant[:id]
222
+ when Integer, String then tenant
223
+ else
224
+ tenant.try(:llm_tenant_id) || tenant.try(:id)
225
+ end
226
226
  end
227
227
 
228
228
  # Budget tracking
@@ -327,7 +327,7 @@ module RubyLLM
327
327
  else
328
328
  RubyLLM::Agents::Execution.create!(execution_data)
329
329
  end
330
- rescue StandardError => e
330
+ rescue => e
331
331
  Rails.logger.error("[RubyLLM::Agents] Failed to record pipeline execution: #{e.message}") if defined?(Rails)
332
332
  end
333
333
 
@@ -359,7 +359,7 @@ module RubyLLM
359
359
  else
360
360
  RubyLLM::Agents::Execution.create!(execution_data)
361
361
  end
362
- rescue StandardError => e
362
+ rescue => e
363
363
  Rails.logger.error("[RubyLLM::Agents] Failed to record failed pipeline execution: #{e.message}") if defined?(Rails)
364
364
  end
365
365
 
@@ -63,7 +63,7 @@ module RubyLLM
63
63
  super
64
64
  # Copy steps to subclass
65
65
  subclass.instance_variable_set(:@steps, @steps&.dup || [])
66
- subclass.instance_variable_set(:@callbacks, @callbacks&.dup || { before: [], after: [] })
66
+ subclass.instance_variable_set(:@callbacks, @callbacks&.dup || {before: [], after: []})
67
67
  subclass.instance_variable_set(:@version, @version)
68
68
  subclass.instance_variable_set(:@description, @description)
69
69
  subclass.instance_variable_set(:@cache_ttl, @cache_ttl)
@@ -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
@@ -29,7 +29,7 @@ module RubyLLM
29
29
  def scale(value = nil)
30
30
  if value
31
31
  unless VALID_SCALES.include?(value)
32
- raise ArgumentError, "Scale must be one of: #{VALID_SCALES.join(', ')}"
32
+ raise ArgumentError, "Scale must be one of: #{VALID_SCALES.join(", ")}"
33
33
  end
34
34
  @scale = value
35
35
  else
@@ -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
@@ -126,7 +126,7 @@ module RubyLLM
126
126
  def build_error_result(error, started_at)
127
127
  ImageUpscaleResult.new(
128
128
  image: nil,
129
- source_image: self.image,
129
+ source_image: image,
130
130
  model_id: resolve_model,
131
131
  scale: resolve_scale,
132
132
  output_size: nil,
@@ -160,10 +160,8 @@ module RubyLLM
160
160
  elsif defined?(Vips)
161
161
  img = Vips::Image.new_from_file(image)
162
162
  [img.width, img.height]
163
- else
164
- nil
165
163
  end
166
- rescue StandardError
164
+ rescue
167
165
  nil
168
166
  end
169
167
 
@@ -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
@@ -43,7 +43,7 @@ module RubyLLM
43
43
 
44
44
  # Store in cache for dashboard display
45
45
  store_for_dashboard(event, full_payload)
46
- rescue StandardError => e
46
+ rescue => e
47
47
  Rails.logger.error("[RubyLLM::Agents::AlertManager] Failed: #{e.message}")
48
48
  end
49
49
 
@@ -72,7 +72,7 @@ module RubyLLM
72
72
  return unless handler.respond_to?(:call)
73
73
 
74
74
  handler.call(event, payload)
75
- rescue StandardError => e
75
+ rescue => e
76
76
  Rails.logger.warn("[RubyLLM::Agents::AlertManager] Handler failed: #{e.message}")
77
77
  end
78
78
 
@@ -83,7 +83,7 @@ module RubyLLM
83
83
  # @return [void]
84
84
  def emit_notification(event, payload)
85
85
  ActiveSupport::Notifications.instrument("ruby_llm_agents.alert.#{event}", payload)
86
- rescue StandardError
86
+ rescue
87
87
  # Ignore notification failures
88
88
  end
89
89
 
@@ -106,7 +106,7 @@ module RubyLLM
106
106
  alerts = alerts.first(50)
107
107
 
108
108
  cache.write(key, alerts, expires_in: 24.hours)
109
- rescue StandardError
109
+ rescue
110
110
  # Ignore cache failures
111
111
  end
112
112
 
@@ -235,7 +235,7 @@ module RubyLLM
235
235
  def safe_value(response, method, default = nil)
236
236
  return default unless response.respond_to?(method)
237
237
  response.public_send(method)
238
- rescue StandardError
238
+ rescue
239
239
  default
240
240
  end
241
241
 
@@ -249,7 +249,7 @@ module RubyLLM
249
249
  model_id: attempt[:model_id],
250
250
  attempt_index: @attempts.length
251
251
  )
252
- rescue StandardError
252
+ rescue
253
253
  # Ignore notification failures
254
254
  end
255
255
 
@@ -275,7 +275,7 @@ module RubyLLM
275
275
  end
276
276
 
277
277
  ActiveSupport::Notifications.instrument(event, payload)
278
- rescue StandardError
278
+ rescue
279
279
  # Ignore notification failures
280
280
  end
281
281
 
@@ -288,7 +288,7 @@ module RubyLLM
288
288
  "ruby_llm_agents.attempt.short_circuit",
289
289
  model_id: attempt[:model_id]
290
290
  )
291
- rescue StandardError
291
+ rescue
292
292
  # Ignore notification failures
293
293
  end
294
294
  end
@@ -58,9 +58,9 @@ module RubyLLM
58
58
  # @return [Float] Total spend in USD
59
59
  def current_global_spend(period)
60
60
  total = RubyLLM::Agents::Execution
61
- .where("created_at >= ?", period_start(period))
62
- .where(tenant_id: nil)
63
- .sum(:total_cost)
61
+ .where("created_at >= ?", period_start(period))
62
+ .where(tenant_id: nil)
63
+ .sum(:total_cost)
64
64
  key = SpendRecorder.budget_cache_key(:global, period)
65
65
  BudgetQuery.cache_write(key, total, expires_in: period_ttl(period))
66
66
  total
@@ -72,9 +72,9 @@ module RubyLLM
72
72
  # @return [Integer] Total tokens used
73
73
  def current_global_tokens(period)
74
74
  total = RubyLLM::Agents::Execution
75
- .where("created_at >= ?", period_start(period))
76
- .where(tenant_id: nil)
77
- .sum(:total_tokens)
75
+ .where("created_at >= ?", period_start(period))
76
+ .where(tenant_id: nil)
77
+ .sum(:total_tokens)
78
78
  key = SpendRecorder.token_cache_key(period)
79
79
  BudgetQuery.cache_write(key, total, expires_in: period_ttl(period))
80
80
  total
@@ -106,7 +106,7 @@ module RubyLLM
106
106
  # @param tenant_id [String, nil] The tenant identifier
107
107
  # @param budget_config [Hash] Budget configuration
108
108
  # @return [Float, nil] Remaining budget in USD, or nil if no limit configured
109
- def remaining_budget(scope, period, agent_type: nil, tenant_id: nil, budget_config:)
109
+ def remaining_budget(scope, period, budget_config:, agent_type: nil, tenant_id: nil)
110
110
  limit = case [scope, period]
111
111
  when [:global, :daily]
112
112
  budget_config[:global_daily]
@@ -129,7 +129,7 @@ module RubyLLM
129
129
  # @param tenant_id [String, nil] The tenant identifier
130
130
  # @param budget_config [Hash] Budget configuration
131
131
  # @return [Integer, nil] Remaining token budget, or nil if no limit configured
132
- def remaining_token_budget(period, tenant_id: nil, budget_config:)
132
+ def remaining_token_budget(period, budget_config:, tenant_id: nil)
133
133
  limit = case period
134
134
  when :daily
135
135
  budget_config[:global_daily_tokens]
@@ -148,7 +148,7 @@ module RubyLLM
148
148
  # @param tenant_id [String, nil] The tenant identifier
149
149
  # @param budget_config [Hash] Budget configuration
150
150
  # @return [Hash] Budget status information
151
- def status(agent_type: nil, tenant_id: nil, budget_config:)
151
+ def status(budget_config:, agent_type: nil, tenant_id: nil)
152
152
  {
153
153
  tenant_id: tenant_id,
154
154
  enabled: budget_config[:enabled],
@@ -120,7 +120,7 @@ module RubyLLM
120
120
  return nil unless tenant_budget_table_exists?
121
121
 
122
122
  TenantBudget.for_tenant(tenant_id)
123
- rescue StandardError => e
123
+ rescue => e
124
124
  Rails.logger.warn("[RubyLLM::Agents] Failed to lookup tenant budget: #{e.message}")
125
125
  nil
126
126
  end
@@ -133,8 +133,8 @@ module RubyLLM
133
133
 
134
134
  # Check for new table name (tenants) or old table name (tenant_budgets) for backward compatibility
135
135
  @tenant_budget_table_exists = ::ActiveRecord::Base.connection.table_exists?(:ruby_llm_agents_tenants) ||
136
- ::ActiveRecord::Base.connection.table_exists?(:ruby_llm_agents_tenant_budgets)
137
- rescue StandardError
136
+ ::ActiveRecord::Base.connection.table_exists?(:ruby_llm_agents_tenant_budgets)
137
+ rescue
138
138
  @tenant_budget_table_exists = false
139
139
  end
140
140
 
@@ -13,7 +13,7 @@ module RubyLLM
13
13
  # @param tenant_id [String, nil] The tenant identifier
14
14
  # @param budget_config [Hash] Budget configuration
15
15
  # @return [Hash, nil] Forecast information
16
- def calculate_forecast(tenant_id: nil, budget_config:)
16
+ def calculate_forecast(budget_config:, tenant_id: nil)
17
17
  return nil unless budget_config[:enabled]
18
18
  return nil unless budget_config[:global_daily] || budget_config[:global_monthly]
19
19
 
@@ -61,7 +61,7 @@ module RubyLLM
61
61
  # @return [Float] New total
62
62
  def increment_spend(scope, period, amount, agent_type: nil, tenant_id: nil)
63
63
  key = budget_cache_key(scope, period, agent_type: agent_type, tenant_id: tenant_id)
64
- ttl = period == :daily ? 1.day : 31.days
64
+ ttl = (period == :daily) ? 1.day : 31.days
65
65
 
66
66
  # Read-modify-write for float values (cache increment is for integers)
67
67
  current = (SpendRecorder.cache_read(key) || 0).to_f
@@ -81,7 +81,7 @@ module RubyLLM
81
81
  def increment_tokens(scope, period, tokens, agent_type: nil, tenant_id: nil)
82
82
  # For now, we only track global token usage (not per-agent)
83
83
  key = token_cache_key(period, tenant_id: tenant_id)
84
- ttl = period == :daily ? 1.day : 31.days
84
+ ttl = (period == :daily) ? 1.day : 31.days
85
85
 
86
86
  current = (SpendRecorder.cache_read(key) || 0).to_i
87
87
  new_total = current + tokens
@@ -102,7 +102,7 @@ module RubyLLM
102
102
  # @param period [Symbol] :daily or :monthly
103
103
  # @return [String] Date string
104
104
  def date_key_part(period)
105
- period == :daily ? Date.current.to_s : Date.current.strftime("%Y-%m")
105
+ (period == :daily) ? Date.current.to_s : Date.current.strftime("%Y-%m")
106
106
  end
107
107
 
108
108
  # Generates an alert cache key
@@ -156,28 +156,28 @@ module RubyLLM
156
156
  def check_soft_cap_alerts(agent_type, tenant_id, budget_config)
157
157
  # Check global daily
158
158
  check_budget_alert(:global_daily, budget_config[:global_daily],
159
- BudgetQuery.current_spend(:global, :daily, tenant_id: tenant_id),
160
- agent_type, tenant_id, budget_config)
159
+ BudgetQuery.current_spend(:global, :daily, tenant_id: tenant_id),
160
+ agent_type, tenant_id, budget_config)
161
161
 
162
162
  # Check global monthly
163
163
  check_budget_alert(:global_monthly, budget_config[:global_monthly],
164
- BudgetQuery.current_spend(:global, :monthly, tenant_id: tenant_id),
165
- agent_type, tenant_id, budget_config)
164
+ BudgetQuery.current_spend(:global, :monthly, tenant_id: tenant_id),
165
+ agent_type, tenant_id, budget_config)
166
166
 
167
167
  # Check per-agent daily
168
168
  agent_daily_limit = budget_config[:per_agent_daily]&.dig(agent_type)
169
169
  if agent_daily_limit
170
170
  check_budget_alert(:per_agent_daily, agent_daily_limit,
171
- BudgetQuery.current_spend(:agent, :daily, agent_type: agent_type, tenant_id: tenant_id),
172
- agent_type, tenant_id, budget_config)
171
+ BudgetQuery.current_spend(:agent, :daily, agent_type: agent_type, tenant_id: tenant_id),
172
+ agent_type, tenant_id, budget_config)
173
173
  end
174
174
 
175
175
  # Check per-agent monthly
176
176
  agent_monthly_limit = budget_config[:per_agent_monthly]&.dig(agent_type)
177
177
  if agent_monthly_limit
178
178
  check_budget_alert(:per_agent_monthly, agent_monthly_limit,
179
- BudgetQuery.current_spend(:agent, :monthly, agent_type: agent_type, tenant_id: tenant_id),
180
- agent_type, tenant_id, budget_config)
179
+ BudgetQuery.current_spend(:agent, :monthly, agent_type: agent_type, tenant_id: tenant_id),
180
+ agent_type, tenant_id, budget_config)
181
181
  end
182
182
  end
183
183
 
@@ -194,7 +194,7 @@ module RubyLLM
194
194
  return unless limit
195
195
  return if current <= limit
196
196
 
197
- event = budget_config[:enforcement] == :hard ? :budget_hard_cap : :budget_soft_cap
197
+ event = (budget_config[:enforcement] == :hard) ? :budget_hard_cap : :budget_soft_cap
198
198
 
199
199
  # Prevent duplicate alerts by using a cache key (include tenant for isolation)
200
200
  key = alert_cache_key("budget_alert", scope, tenant_id)
@@ -221,13 +221,13 @@ module RubyLLM
221
221
  def check_soft_token_alerts(agent_type, tenant_id, budget_config)
222
222
  # Check global daily tokens
223
223
  check_token_alert(:global_daily_tokens, budget_config[:global_daily_tokens],
224
- BudgetQuery.current_tokens(:daily, tenant_id: tenant_id),
225
- agent_type, tenant_id, budget_config)
224
+ BudgetQuery.current_tokens(:daily, tenant_id: tenant_id),
225
+ agent_type, tenant_id, budget_config)
226
226
 
227
227
  # Check global monthly tokens
228
228
  check_token_alert(:global_monthly_tokens, budget_config[:global_monthly_tokens],
229
- BudgetQuery.current_tokens(:monthly, tenant_id: tenant_id),
230
- agent_type, tenant_id, budget_config)
229
+ BudgetQuery.current_tokens(:monthly, tenant_id: tenant_id),
230
+ agent_type, tenant_id, budget_config)
231
231
  end
232
232
 
233
233
  # Checks if a token alert should be fired
@@ -243,7 +243,7 @@ module RubyLLM
243
243
  return unless limit
244
244
  return if current <= limit
245
245
 
246
- event = budget_config[:enforcement] == :hard ? :token_hard_cap : :token_soft_cap
246
+ event = (budget_config[:enforcement] == :hard) ? :token_hard_cap : :token_soft_cap
247
247
 
248
248
  # Prevent duplicate alerts
249
249
  key = alert_cache_key("token_alert", scope, tenant_id)
@@ -30,6 +30,7 @@ module RubyLLM
30
30
  # @api public
31
31
  class CircuitBreaker
32
32
  include CacheHelper
33
+
33
34
  attr_reader :agent_type, :model_id, :tenant_id, :errors_threshold, :window_seconds, :cooldown_seconds
34
35
 
35
36
  # @param agent_type [String] The agent class name
@@ -33,7 +33,7 @@ module RubyLLM
33
33
  execution = Execution.create!(filtered_data)
34
34
 
35
35
  # Create detail record if present
36
- if detail_data && detail_data.values.any? { |v| v.present? && v != {} && v != [] }
36
+ if detail_data&.values&.any? { |v| v.present? && v != {} && v != [] }
37
37
  execution.create_detail!(detail_data)
38
38
  end
39
39
 
@@ -108,11 +108,11 @@ module RubyLLM
108
108
 
109
109
  @attempts.filter_map do |attempt|
110
110
  if attempt["short_circuited"]
111
- { model: attempt["model_id"], error_class: "CircuitBreakerOpen", error_message: "circuit breaker open",
112
- error_backtrace: nil }
111
+ {model: attempt["model_id"], error_class: "CircuitBreakerOpen", error_message: "circuit breaker open",
112
+ error_backtrace: nil}
113
113
  elsif attempt["error_class"]
114
- { model: attempt["model_id"], error_class: attempt["error_class"], error_message: attempt["error_message"],
115
- error_backtrace: attempt["error_backtrace"] }
114
+ {model: attempt["model_id"], error_class: attempt["error_class"], error_message: attempt["error_message"],
115
+ error_backtrace: attempt["error_backtrace"]}
116
116
  end
117
117
  end
118
118
  end
@@ -125,11 +125,11 @@ module RubyLLM
125
125
  if attempt["short_circuited"]
126
126
  parts << " #{model}: circuit breaker open"
127
127
  elsif attempt["error_class"]
128
- parts << " #{model}: #{attempt['error_class']} - #{attempt['error_message']}"
128
+ parts << " #{model}: #{attempt["error_class"]} - #{attempt["error_message"]}"
129
129
  end
130
130
  end
131
131
  else
132
- parts << " Models: #{@models_tried.join(', ')}"
132
+ parts << " Models: #{@models_tried.join(", ")}"
133
133
  parts << " Last error: #{@last_error.message}"
134
134
  end
135
135
  parts.join("\n")
@@ -164,7 +164,7 @@ module RubyLLM
164
164
  # @return [Boolean]
165
165
  def budgets_enabled?
166
166
  RubyLLM::Agents.configuration.budgets_enabled?
167
- rescue StandardError
167
+ rescue
168
168
  false
169
169
  end
170
170
 
@@ -176,7 +176,7 @@ module RubyLLM
176
176
  return false unless agent_class
177
177
 
178
178
  agent_class.respond_to?(:cache_enabled?) && agent_class.cache_enabled?
179
- rescue StandardError
179
+ rescue
180
180
  false
181
181
  end
182
182
 
@@ -192,20 +192,20 @@ module RubyLLM
192
192
  return false unless agent_class
193
193
 
194
194
  retries = if agent_class.respond_to?(:retries)
195
- agent_class.retries
196
- else
197
- 0
198
- end
195
+ agent_class.retries
196
+ else
197
+ 0
198
+ end
199
199
 
200
200
  fallbacks = if agent_class.respond_to?(:fallback_models)
201
- agent_class.fallback_models
202
- else
203
- []
204
- end
201
+ agent_class.fallback_models
202
+ else
203
+ []
204
+ end
205
205
 
206
206
  (retries.is_a?(Integer) && retries.positive?) ||
207
207
  (fallbacks.is_a?(Array) && fallbacks.any?)
208
- rescue StandardError
208
+ rescue
209
209
  false
210
210
  end
211
211
  end
@@ -51,7 +51,7 @@ module RubyLLM
51
51
  # @return [Boolean]
52
52
  def budgets_enabled?
53
53
  global_config.budgets_enabled?
54
- rescue StandardError
54
+ rescue
55
55
  false
56
56
  end
57
57
 
@@ -78,7 +78,7 @@ module RubyLLM
78
78
  )
79
79
  rescue RubyLLM::Agents::Reliability::BudgetExceededError
80
80
  raise
81
- rescue StandardError => e
81
+ rescue => e
82
82
  error("Budget check failed: #{e.message}")
83
83
  end
84
84
 
@@ -117,7 +117,7 @@ module RubyLLM
117
117
  tenant_id: context.tenant_id
118
118
  )
119
119
  end
120
- rescue StandardError => e
120
+ rescue => e
121
121
  error("Failed to record spend: #{e.message}")
122
122
  end
123
123
  end
@@ -71,7 +71,7 @@ module RubyLLM
71
71
  # @return [ActiveSupport::Cache::Store, nil]
72
72
  def cache_store
73
73
  global_config.cache_store
74
- rescue StandardError
74
+ rescue
75
75
  nil
76
76
  end
77
77
 
@@ -130,7 +130,7 @@ module RubyLLM
130
130
  else
131
131
  input.to_json
132
132
  end
133
- rescue StandardError
133
+ rescue
134
134
  input.to_s
135
135
  end
136
136
 
@@ -140,7 +140,7 @@ module RubyLLM
140
140
  # @return [Object, nil] Cached value or nil
141
141
  def cache_read(key)
142
142
  cache_store.read(key)
143
- rescue StandardError => e
143
+ rescue => e
144
144
  error("Cache read failed: #{e.message}")
145
145
  nil
146
146
  end
@@ -154,7 +154,7 @@ module RubyLLM
154
154
  options[:expires_in] = cache_ttl if cache_ttl
155
155
 
156
156
  cache_store.write(key, value, **options)
157
- rescue StandardError => e
157
+ rescue => e
158
158
  error("Cache write failed: #{e.message}")
159
159
  end
160
160
  end