ruby_llm-agents 3.1.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 (85) 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/lib/generators/ruby_llm_agents/agent_generator.rb +4 -4
  15. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +6 -6
  16. data/lib/generators/ruby_llm_agents/embedder_generator.rb +4 -4
  17. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -7
  18. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +4 -4
  19. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +6 -6
  20. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +9 -9
  21. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +6 -6
  22. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +4 -4
  23. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +4 -4
  24. data/lib/generators/ruby_llm_agents/install_generator.rb +3 -3
  25. data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +4 -4
  26. data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +2 -2
  27. data/lib/generators/ruby_llm_agents/restructure_generator.rb +13 -13
  28. data/lib/generators/ruby_llm_agents/speaker_generator.rb +6 -6
  29. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +4 -4
  30. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +2 -2
  31. data/lib/ruby_llm/agents/audio/speaker.rb +40 -31
  32. data/lib/ruby_llm/agents/audio/speech_client.rb +328 -0
  33. data/lib/ruby_llm/agents/audio/speech_pricing.rb +273 -0
  34. data/lib/ruby_llm/agents/audio/transcriber.rb +33 -33
  35. data/lib/ruby_llm/agents/base_agent.rb +14 -14
  36. data/lib/ruby_llm/agents/core/base/callbacks.rb +3 -3
  37. data/lib/ruby_llm/agents/core/configuration.rb +86 -73
  38. data/lib/ruby_llm/agents/core/errors.rb +27 -2
  39. data/lib/ruby_llm/agents/core/instrumentation.rb +64 -66
  40. data/lib/ruby_llm/agents/core/llm_tenant.rb +7 -7
  41. data/lib/ruby_llm/agents/core/version.rb +1 -1
  42. data/lib/ruby_llm/agents/dsl/base.rb +3 -3
  43. data/lib/ruby_llm/agents/dsl/reliability.rb +9 -9
  44. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +1 -1
  45. data/lib/ruby_llm/agents/image/analyzer/execution.rb +4 -4
  46. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +1 -1
  47. data/lib/ruby_llm/agents/image/background_remover/execution.rb +3 -3
  48. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +8 -8
  49. data/lib/ruby_llm/agents/image/editor/execution.rb +1 -1
  50. data/lib/ruby_llm/agents/image/generator/pricing.rb +9 -10
  51. data/lib/ruby_llm/agents/image/generator.rb +6 -6
  52. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +6 -6
  53. data/lib/ruby_llm/agents/image/pipeline/execution.rb +9 -9
  54. data/lib/ruby_llm/agents/image/pipeline.rb +1 -1
  55. data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -1
  56. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +1 -1
  57. data/lib/ruby_llm/agents/image/upscaler/execution.rb +3 -5
  58. data/lib/ruby_llm/agents/image/variator/execution.rb +1 -1
  59. data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +4 -4
  60. data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +4 -4
  61. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +9 -9
  62. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +3 -3
  63. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +1 -1
  64. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +17 -17
  65. data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +1 -0
  66. data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +1 -1
  67. data/lib/ruby_llm/agents/infrastructure/reliability.rb +6 -6
  68. data/lib/ruby_llm/agents/pipeline/builder.rb +11 -11
  69. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +3 -3
  70. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +4 -4
  71. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +34 -22
  72. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +2 -3
  73. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +7 -7
  74. data/lib/ruby_llm/agents/results/background_removal_result.rb +6 -6
  75. data/lib/ruby_llm/agents/results/embedding_result.rb +15 -15
  76. data/lib/ruby_llm/agents/results/image_analysis_result.rb +7 -7
  77. data/lib/ruby_llm/agents/results/image_edit_result.rb +4 -4
  78. data/lib/ruby_llm/agents/results/image_generation_result.rb +5 -5
  79. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +4 -4
  80. data/lib/ruby_llm/agents/results/image_transform_result.rb +4 -4
  81. data/lib/ruby_llm/agents/results/image_upscale_result.rb +5 -5
  82. data/lib/ruby_llm/agents/results/image_variation_result.rb +4 -4
  83. data/lib/ruby_llm/agents/results/transcription_result.rb +1 -1
  84. data/lib/ruby_llm/agents/text/embedder.rb +13 -13
  85. metadata +3 -1
@@ -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
@@ -52,10 +52,10 @@ module RubyLLM
52
52
  begin
53
53
  complete_execution(execution, context, status: "success")
54
54
  status_update_completed = true
55
- rescue StandardError
55
+ rescue
56
56
  # Let ensure block handle via mark_execution_failed!
57
57
  end
58
- rescue StandardError => e
58
+ rescue => e
59
59
  context.completed_at = Time.current
60
60
  context.error = e
61
61
  raised_exception = e
@@ -63,7 +63,7 @@ module RubyLLM
63
63
  begin
64
64
  complete_execution(execution, context, status: determine_error_status(e))
65
65
  status_update_completed = true
66
- rescue StandardError
66
+ rescue
67
67
  # Let ensure block handle via mark_execution_failed!
68
68
  end
69
69
 
@@ -100,7 +100,7 @@ module RubyLLM
100
100
  end
101
101
 
102
102
  execution
103
- rescue StandardError => e
103
+ rescue => e
104
104
  error("Failed to create running execution record: #{e.message}")
105
105
  nil
106
106
  end
@@ -131,7 +131,7 @@ module RubyLLM
131
131
 
132
132
  # Save detail data (prompts, responses, tool calls, etc.)
133
133
  save_execution_details(execution, context, status)
134
- rescue StandardError => e
134
+ rescue => e
135
135
  error("Failed to complete execution record: #{e.message}")
136
136
  raise # Re-raise for ensure block to handle via mark_execution_failed!
137
137
  end
@@ -160,16 +160,16 @@ module RubyLLM
160
160
 
161
161
  # Store error_message in detail table (best-effort)
162
162
  begin
163
- detail_attrs = { error_message: error_message }
163
+ detail_attrs = {error_message: error_message}
164
164
  if execution.detail
165
165
  execution.detail.update_columns(detail_attrs)
166
166
  else
167
167
  RubyLLM::Agents::ExecutionDetail.create!(detail_attrs.merge(execution_id: execution.id))
168
168
  end
169
- rescue StandardError
169
+ rescue
170
170
  # Non-critical
171
171
  end
172
- rescue StandardError => e
172
+ rescue => e
173
173
  error("CRITICAL: Failed emergency status update for execution #{execution&.id}: #{e.message}")
174
174
  end
175
175
 
@@ -223,7 +223,11 @@ module RubyLLM
223
223
  }
224
224
 
225
225
  # Store niche cache key in metadata
226
- merged_metadata = context.metadata.dup rescue {}
226
+ merged_metadata = begin
227
+ context.metadata.dup
228
+ rescue
229
+ {}
230
+ end
227
231
  if context.cached? && context[:cache_key]
228
232
  merged_metadata["response_cache_key"] = context[:cache_key]
229
233
  end
@@ -284,7 +288,7 @@ module RubyLLM
284
288
  else
285
289
  execution.create_detail!(detail_data)
286
290
  end
287
- rescue StandardError => e
291
+ rescue => e
288
292
  error("Failed to save execution details: #{e.message}")
289
293
  end
290
294
 
@@ -304,7 +308,7 @@ module RubyLLM
304
308
  else
305
309
  create_execution_record(data)
306
310
  end
307
- rescue StandardError => e
311
+ rescue => e
308
312
  error("Failed to record execution: #{e.message}")
309
313
  end
310
314
 
@@ -314,7 +318,11 @@ module RubyLLM
314
318
  # @param status [String] "success" or "error"
315
319
  # @return [Hash] Execution data
316
320
  def build_execution_data(context, status)
317
- merged_metadata = context.metadata.dup rescue {}
321
+ merged_metadata = begin
322
+ context.metadata.dup
323
+ rescue
324
+ {}
325
+ end
318
326
  if context.cached? && context[:cache_key]
319
327
  merged_metadata["response_cache_key"] = context[:cache_key]
320
328
  end
@@ -355,7 +363,7 @@ module RubyLLM
355
363
  end
356
364
 
357
365
  # Store detail data for separate creation
358
- detail_data = { parameters: sanitize_parameters(context) }
366
+ detail_data = {parameters: sanitize_parameters(context)}
359
367
  if global_config.persist_prompts
360
368
  exec_opts = context.options[:options] || {}
361
369
  detail_data[:system_prompt] = exec_opts[:system_prompt]
@@ -396,7 +404,11 @@ module RubyLLM
396
404
  def sanitize_parameters(context)
397
405
  return {} unless context.agent_instance.respond_to?(:options, true)
398
406
 
399
- params = context.agent_instance.send(:options) rescue {}
407
+ params = begin
408
+ context.agent_instance.send(:options)
409
+ rescue
410
+ {}
411
+ end
400
412
  params = params.dup
401
413
  params.transform_keys!(&:to_s)
402
414
 
@@ -421,7 +433,7 @@ module RubyLLM
421
433
  return "" if message.nil?
422
434
 
423
435
  message.to_s.truncate(1000)
424
- rescue StandardError
436
+ rescue
425
437
  message.to_s[0, 1000]
426
438
  end
427
439
 
@@ -436,7 +448,7 @@ module RubyLLM
436
448
  return nil if content.nil?
437
449
 
438
450
  # Build response hash similar to core instrumentation
439
- response_data = { content: content }
451
+ response_data = {content: content}
440
452
 
441
453
  # Add model_id if available
442
454
  response_data[:model_id] = context.model_used if context.model_used
@@ -446,7 +458,7 @@ module RubyLLM
446
458
  response_data[:output_tokens] = context.output_tokens if context.output_tokens
447
459
 
448
460
  response_data
449
- rescue StandardError => e
461
+ rescue => e
450
462
  error("Failed to serialize response: #{e.message}")
451
463
  nil
452
464
  end
@@ -464,7 +476,7 @@ module RubyLLM
464
476
  def create_execution_record(data)
465
477
  detail_data = data.delete(:_detail_data)
466
478
  execution = Execution.create!(data)
467
- if detail_data && detail_data.values.any? { |v| v.present? && v != {} && v != [] }
479
+ if detail_data&.values&.any? { |v| v.present? && v != {} && v != [] }
468
480
  execution.create_detail!(detail_data)
469
481
  end
470
482
  execution
@@ -487,7 +499,7 @@ module RubyLLM
487
499
  else
488
500
  cfg.track_executions
489
501
  end
490
- rescue StandardError
502
+ rescue
491
503
  false
492
504
  end
493
505
 
@@ -496,7 +508,7 @@ module RubyLLM
496
508
  # @return [Boolean]
497
509
  def track_cache_hits?
498
510
  global_config.respond_to?(:track_cache_hits) && global_config.track_cache_hits
499
- rescue StandardError
511
+ rescue
500
512
  false
501
513
  end
502
514
 
@@ -505,7 +517,7 @@ module RubyLLM
505
517
  # @return [Boolean]
506
518
  def async_logging?
507
519
  global_config.async_logging && defined?(Infrastructure::ExecutionLoggerJob)
508
- rescue StandardError
520
+ rescue
509
521
  false
510
522
  end
511
523
 
@@ -520,7 +532,7 @@ module RubyLLM
520
532
  @_assistant_prompt_column_exists = begin
521
533
  defined?(RubyLLM::Agents::ExecutionDetail) &&
522
534
  RubyLLM::Agents::ExecutionDetail.column_names.include?("assistant_prompt")
523
- rescue StandardError
535
+ rescue
524
536
  false
525
537
  end
526
538
  end
@@ -172,8 +172,7 @@ module RubyLLM
172
172
  tracker.complete_attempt(attempt, success: true, response: context.output)
173
173
 
174
174
  return context
175
-
176
- rescue StandardError => e
175
+ rescue => e
177
176
  context.error = e
178
177
  breaker&.record_failure!
179
178
  tracker.complete_attempt(attempt, success: false, error: e)
@@ -305,7 +304,7 @@ module RubyLLM
305
304
  else
306
305
  sleep(seconds)
307
306
  end
308
- rescue StandardError
307
+ rescue
309
308
  # Fall back to regular sleep if async detection fails
310
309
  sleep(seconds)
311
310
  end
@@ -79,8 +79,8 @@ module RubyLLM
79
79
  context.tenant_config = extract_tenant_config(tenant_value)
80
80
  else
81
81
  raise ArgumentError,
82
- "tenant must respond to :llm_tenant_id (use llm_tenant DSL), " \
83
- "or be a Hash with :id key, got #{tenant_value.class}"
82
+ "tenant must respond to :llm_tenant_id (use llm_tenant DSL), " \
83
+ "or be a Hash with :id key, got #{tenant_value.class}"
84
84
  end
85
85
  end
86
86
  end
@@ -106,7 +106,7 @@ module RubyLLM
106
106
  # For hash-based or string tenants, ensure a minimal record exists
107
107
  RubyLLM::Agents::Tenant.find_or_create_by!(tenant_id: context.tenant_id)
108
108
  end
109
- rescue StandardError => e
109
+ rescue => e
110
110
  # Don't fail the execution if tenant record creation fails
111
111
  log_tenant_warning("ensure tenant record", e)
112
112
  end
@@ -117,7 +117,7 @@ module RubyLLM
117
117
  def ensure_tenant_for_model!(tenant_object)
118
118
  # Check polymorphic link first, then tenant_id
119
119
  existing = RubyLLM::Agents::Tenant.find_by(tenant_record: tenant_object) ||
120
- RubyLLM::Agents::Tenant.find_by(tenant_id: tenant_object.llm_tenant_id)
120
+ RubyLLM::Agents::Tenant.find_by(tenant_id: tenant_object.llm_tenant_id)
121
121
  return if existing
122
122
 
123
123
  options = tenant_object.class.try(:llm_tenant_options) || {}
@@ -146,7 +146,7 @@ module RubyLLM
146
146
  return @tenant_table_exists if defined?(@tenant_table_exists)
147
147
 
148
148
  @tenant_table_exists = ::ActiveRecord::Base.connection.table_exists?(:ruby_llm_agents_tenants)
149
- rescue StandardError
149
+ rescue
150
150
  @tenant_table_exists = false
151
151
  end
152
152
 
@@ -181,7 +181,7 @@ module RubyLLM
181
181
  return if api_keys.blank?
182
182
 
183
183
  apply_api_keys_to_ruby_llm(api_keys)
184
- rescue StandardError => e
184
+ rescue => e
185
185
  # Log but don't fail if API key extraction fails
186
186
  warn_api_key_error("tenant object", e)
187
187
  end
@@ -228,7 +228,7 @@ module RubyLLM
228
228
  return nil unless tenant.respond_to?(:llm_config)
229
229
 
230
230
  tenant.llm_config
231
- rescue StandardError
231
+ rescue
232
232
  nil
233
233
  end
234
234
  end
@@ -16,9 +16,9 @@ module RubyLLM
16
16
  #
17
17
  class BackgroundRemovalResult
18
18
  attr_reader :foreground, :mask, :source_image, :model_id, :output_format,
19
- :alpha_matting, :refine_edges,
20
- :started_at, :completed_at, :tenant_id, :remover_class,
21
- :error_class, :error_message
19
+ :alpha_matting, :refine_edges,
20
+ :started_at, :completed_at, :tenant_id, :remover_class,
21
+ :error_class, :error_message
22
22
 
23
23
  # Initialize a new result
24
24
  #
@@ -36,8 +36,8 @@ module RubyLLM
36
36
  # @param error_class [String, nil] Error class name if failed
37
37
  # @param error_message [String, nil] Error message if failed
38
38
  def initialize(foreground:, mask:, source_image:, model_id:, output_format:,
39
- alpha_matting:, refine_edges:, started_at:, completed_at:,
40
- tenant_id:, remover_class:, error_class: nil, error_message: nil)
39
+ alpha_matting:, refine_edges:, started_at:, completed_at:,
40
+ tenant_id:, remover_class:, error_class: nil, error_message: nil)
41
41
  @foreground = foreground
42
42
  @mask = mask
43
43
  @source_image = source_image
@@ -224,7 +224,7 @@ module RubyLLM
224
224
  # Lightweight result for cached removals
225
225
  class CachedBackgroundRemovalResult
226
226
  attr_reader :url, :data, :mask_url, :mask_data, :mime_type, :model_id,
227
- :output_format, :total_cost, :cached_at
227
+ :output_format, :total_cost, :cached_at
228
228
 
229
229
  def initialize(data)
230
230
  @url = data[:url]
@@ -156,13 +156,13 @@ module RubyLLM
156
156
  return nil if v1.nil?
157
157
 
158
158
  v2 = case other
159
- when EmbeddingResult
160
- other.vector
161
- when Array
162
- other
163
- else
164
- raise ArgumentError, "other must be EmbeddingResult or Array, got #{other.class}"
165
- end
159
+ when EmbeddingResult
160
+ other.vector
161
+ when Array
162
+ other
163
+ else
164
+ raise ArgumentError, "other must be EmbeddingResult or Array, got #{other.class}"
165
+ end
166
166
 
167
167
  return nil if v2.nil?
168
168
 
@@ -183,17 +183,17 @@ module RubyLLM
183
183
 
184
184
  similarities = others.each_with_index.map do |other, idx|
185
185
  v2 = case other
186
- when EmbeddingResult
187
- other.vector
188
- when Array
189
- other
190
- else
191
- next nil
192
- end
186
+ when EmbeddingResult
187
+ other.vector
188
+ when Array
189
+ other
190
+ else
191
+ next nil
192
+ end
193
193
 
194
194
  next nil if v2.nil?
195
195
 
196
- { index: idx, similarity: cosine_similarity(v1, v2) }
196
+ {index: idx, similarity: cosine_similarity(v1, v2)}
197
197
  end.compact
198
198
 
199
199
  similarities.sort_by { |s| -s[:similarity] }.first(limit)
@@ -18,9 +18,9 @@ module RubyLLM
18
18
  #
19
19
  class ImageAnalysisResult
20
20
  attr_reader :image, :model_id, :analysis_type,
21
- :caption, :description, :tags, :objects, :colors, :text,
22
- :raw_response, :started_at, :completed_at, :tenant_id, :analyzer_class,
23
- :error_class, :error_message
21
+ :caption, :description, :tags, :objects, :colors, :text,
22
+ :raw_response, :started_at, :completed_at, :tenant_id, :analyzer_class,
23
+ :error_class, :error_message
24
24
 
25
25
  # Initialize a new result
26
26
  #
@@ -41,8 +41,8 @@ module RubyLLM
41
41
  # @param error_class [String, nil] Error class name if failed
42
42
  # @param error_message [String, nil] Error message if failed
43
43
  def initialize(image:, model_id:, analysis_type:, caption:, description:, tags:,
44
- objects:, colors:, text:, raw_response:, started_at:, completed_at:,
45
- tenant_id:, analyzer_class:, error_class: nil, error_message: nil)
44
+ objects:, colors:, text:, raw_response:, started_at:, completed_at:,
45
+ tenant_id:, analyzer_class:, error_class: nil, error_message: nil)
46
46
  @image = image
47
47
  @model_id = model_id
48
48
  @analysis_type = analysis_type
@@ -235,8 +235,8 @@ module RubyLLM
235
235
  # Lightweight result for cached analyses
236
236
  class CachedImageAnalysisResult
237
237
  attr_reader :image, :model_id, :analysis_type,
238
- :caption, :description, :tags, :objects, :colors, :text,
239
- :total_cost, :cached_at
238
+ :caption, :description, :tags, :objects, :colors, :text,
239
+ :total_cost, :cached_at
240
240
 
241
241
  def initialize(data)
242
242
  @image = data[:image]
@@ -18,8 +18,8 @@ module RubyLLM
18
18
  #
19
19
  class ImageEditResult
20
20
  attr_reader :images, :source_image, :mask, :prompt, :model_id, :size,
21
- :started_at, :completed_at, :tenant_id, :editor_class,
22
- :error_class, :error_message
21
+ :started_at, :completed_at, :tenant_id, :editor_class,
22
+ :error_class, :error_message
23
23
 
24
24
  # Initialize a new result
25
25
  #
@@ -36,8 +36,8 @@ module RubyLLM
36
36
  # @param error_class [String, nil] Error class name if failed
37
37
  # @param error_message [String, nil] Error message if failed
38
38
  def initialize(images:, source_image:, mask:, prompt:, model_id:, size:,
39
- started_at:, completed_at:, tenant_id:, editor_class:,
40
- error_class: nil, error_message: nil)
39
+ started_at:, completed_at:, tenant_id:, editor_class:,
40
+ error_class: nil, error_message: nil)
41
41
  @images = images
42
42
  @source_image = source_image
43
43
  @mask = mask