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
@@ -390,79 +390,85 @@ module RubyLLM
390
390
  end
391
391
  end
392
392
 
393
+ # Attributes with custom setters (validation) — only readers here
394
+ attr_reader :default_embedding_dimensions, :default_embedding_batch_size
395
+
393
396
  # Attributes without validation (simple accessors)
394
397
  attr_accessor :default_model,
395
- :async_logging,
396
- :async_max_concurrency,
397
- :retention_period,
398
- :dashboard_parent_controller,
399
- :basic_auth_username,
400
- :basic_auth_password,
401
- :default_fallback_models,
402
- :default_total_timeout,
403
- :default_streaming,
404
- :default_tools,
405
- :default_thinking,
406
- :on_alert,
407
- :persist_prompts,
408
- :persist_responses,
409
- :multi_tenancy_enabled,
410
- :persist_messages_summary,
411
- :default_retryable_patterns,
412
- :default_embedding_model,
413
- :default_embedding_dimensions,
414
- :default_embedding_batch_size,
415
- :track_embeddings,
416
- :default_transcription_model,
417
- :track_transcriptions,
418
- :default_tts_provider,
419
- :default_tts_model,
420
- :default_tts_voice,
421
- :track_speech,
422
- :track_executions,
423
- :track_audio,
424
- :track_cache_hits,
425
- :default_image_model,
426
- :default_image_size,
427
- :default_image_quality,
428
- :default_image_style,
429
- :max_image_prompt_length,
430
- :track_image_generation,
431
- :image_model_aliases,
432
- :litellm_pricing_url,
433
- :litellm_pricing_cache_ttl,
434
- :default_image_cost,
435
- :image_model_pricing,
436
- :default_variator_model,
437
- :default_editor_model,
438
- :default_transformer_model,
439
- :default_upscaler_model,
440
- :default_variation_strength,
441
- :default_transform_strength,
442
- :default_analyzer_model,
443
- :default_analysis_type,
444
- :default_analyzer_max_tags,
445
- :default_background_remover_model,
446
- :default_background_output_format,
447
- :root_directory,
448
- :root_namespace,
449
- :tool_result_max_length,
450
- :redaction
398
+ :async_logging,
399
+ :async_max_concurrency,
400
+ :retention_period,
401
+ :dashboard_parent_controller,
402
+ :basic_auth_username,
403
+ :basic_auth_password,
404
+ :default_fallback_models,
405
+ :default_total_timeout,
406
+ :default_streaming,
407
+ :default_tools,
408
+ :default_thinking,
409
+ :on_alert,
410
+ :persist_prompts,
411
+ :persist_responses,
412
+ :multi_tenancy_enabled,
413
+ :persist_messages_summary,
414
+ :default_retryable_patterns,
415
+ :default_embedding_model,
416
+ :track_embeddings,
417
+ :default_transcription_model,
418
+ :track_transcriptions,
419
+ :default_tts_provider,
420
+ :default_tts_model,
421
+ :default_tts_voice,
422
+ :track_speech,
423
+ :elevenlabs_api_key,
424
+ :elevenlabs_api_base,
425
+ :tts_model_pricing,
426
+ :default_tts_cost,
427
+ :track_executions,
428
+ :track_audio,
429
+ :track_cache_hits,
430
+ :default_image_model,
431
+ :default_image_size,
432
+ :default_image_quality,
433
+ :default_image_style,
434
+ :max_image_prompt_length,
435
+ :track_image_generation,
436
+ :image_model_aliases,
437
+ :litellm_pricing_url,
438
+ :litellm_pricing_cache_ttl,
439
+ :default_image_cost,
440
+ :image_model_pricing,
441
+ :default_variator_model,
442
+ :default_editor_model,
443
+ :default_transformer_model,
444
+ :default_upscaler_model,
445
+ :default_variation_strength,
446
+ :default_transform_strength,
447
+ :default_analyzer_model,
448
+ :default_analysis_type,
449
+ :default_analyzer_max_tags,
450
+ :default_background_remover_model,
451
+ :default_background_output_format,
452
+ :root_directory,
453
+ :root_namespace,
454
+ :tool_result_max_length,
455
+ :redaction,
456
+ :persist_audio_data
451
457
 
452
458
  # Attributes with validation (readers only, custom setters below)
453
459
  attr_reader :default_temperature,
454
- :default_timeout,
455
- :anomaly_cost_threshold,
456
- :anomaly_duration_threshold,
457
- :per_page,
458
- :recent_executions_limit,
459
- :job_retry_attempts,
460
- :messages_summary_max_length,
461
- :dashboard_auth,
462
- :tenant_resolver,
463
- :tenant_config_resolver,
464
- :default_retries,
465
- :budgets
460
+ :default_timeout,
461
+ :anomaly_cost_threshold,
462
+ :anomaly_duration_threshold,
463
+ :per_page,
464
+ :recent_executions_limit,
465
+ :job_retry_attempts,
466
+ :messages_summary_max_length,
467
+ :dashboard_auth,
468
+ :tenant_resolver,
469
+ :tenant_config_resolver,
470
+ :default_retries,
471
+ :budgets
466
472
 
467
473
  attr_writer :cache_store
468
474
 
@@ -597,7 +603,7 @@ module RubyLLM
597
603
  # @param value [Integer, nil] Dimensions (must be nil or > 0)
598
604
  # @raise [ArgumentError] If value is not nil or positive
599
605
  def default_embedding_dimensions=(value)
600
- unless value.nil? || (value.is_a?(Numeric) && value > 0)
606
+ if !value.nil? && !(value.is_a?(Numeric) && value > 0)
601
607
  raise ArgumentError, "default_embedding_dimensions must be nil or greater than 0"
602
608
  end
603
609
 
@@ -627,13 +633,13 @@ module RubyLLM
627
633
  @job_retry_attempts = 3
628
634
 
629
635
  # Reliability defaults (all disabled by default for backward compatibility)
630
- @default_retries = { max: 0, backoff: :exponential, base: 0.4, max_delay: 3.0, on: [] }
636
+ @default_retries = {max: 0, backoff: :exponential, base: 0.4, max_delay: 3.0, on: []}
631
637
  @default_fallback_models = []
632
638
  @default_total_timeout = nil
633
639
  @default_retryable_patterns = {
634
640
  rate_limiting: ["rate limit", "rate_limit", "too many requests", "429", "quota"],
635
641
  server_errors: ["500", "502", "503", "504", "service unavailable",
636
- "internal server error", "bad gateway", "gateway timeout"],
642
+ "internal server error", "bad gateway", "gateway timeout"],
637
643
  capacity: ["overloaded", "capacity"]
638
644
  }
639
645
 
@@ -650,7 +656,7 @@ module RubyLLM
650
656
 
651
657
  # Multi-tenancy defaults (disabled for backward compatibility)
652
658
  @multi_tenancy_enabled = false
653
- @tenant_resolver = -> { nil }
659
+ @tenant_resolver = -> {}
654
660
  @tenant_config_resolver = nil
655
661
 
656
662
  # Messages summary defaults
@@ -673,6 +679,14 @@ module RubyLLM
673
679
  @default_tts_voice = "nova"
674
680
  @track_speech = true
675
681
 
682
+ # ElevenLabs defaults
683
+ @elevenlabs_api_key = nil
684
+ @elevenlabs_api_base = "https://api.elevenlabs.io"
685
+
686
+ # TTS pricing defaults
687
+ @tts_model_pricing = {}
688
+ @default_tts_cost = 0.015
689
+
676
690
  # Execution/conversation agent tracking
677
691
  @track_executions = true
678
692
  @track_audio = true
@@ -721,6 +735,9 @@ module RubyLLM
721
735
 
722
736
  # Redaction defaults (disabled by default)
723
737
  @redaction = nil
738
+
739
+ # Audio data persistence (disabled by default — base64 audio can be large)
740
+ @persist_audio_data = false
724
741
  end
725
742
 
726
743
  # Returns the configured cache store, falling back to Rails.cache
@@ -783,7 +800,7 @@ module RubyLLM
783
800
  return false unless async_available?
784
801
 
785
802
  defined?(::Async::Task) && ::Async::Task.current?
786
- rescue StandardError
803
+ rescue
787
804
  false
788
805
  end
789
806
 
@@ -29,7 +29,7 @@ module RubyLLM
29
29
 
30
30
  def initialize(message = nil, model: nil)
31
31
  @model = model
32
- super(message || "Circuit breaker is open#{model ? " for #{model}" : ""}")
32
+ super(message || "Circuit breaker is open#{" for #{model}" if model}")
33
33
  end
34
34
  end
35
35
 
@@ -78,7 +78,7 @@ module RubyLLM
78
78
  def initialize(message = nil, tenant_id: nil, budget_type: nil)
79
79
  @tenant_id = tenant_id
80
80
  @budget_type = budget_type
81
- super(message || "Budget exceeded#{tenant_id ? " for tenant #{tenant_id}" : ""}")
81
+ super(message || "Budget exceeded#{" for tenant #{tenant_id}" if tenant_id}")
82
82
  end
83
83
  end
84
84
 
@@ -88,5 +88,30 @@ module RubyLLM
88
88
 
89
89
  # Raised for configuration issues
90
90
  class ConfigurationError < Error; end
91
+
92
+ # ============================================================
93
+ # Speech/Audio Errors
94
+ # ============================================================
95
+
96
+ # Raised when a TTS provider is not supported
97
+ class UnsupportedProviderError < Error
98
+ attr_reader :provider
99
+
100
+ def initialize(message = nil, provider: nil)
101
+ @provider = provider
102
+ super(message || "Provider :#{provider} is not supported for this operation")
103
+ end
104
+ end
105
+
106
+ # Raised when the TTS API returns an error response
107
+ class SpeechApiError < Error
108
+ attr_reader :status, :response_body
109
+
110
+ def initialize(message = nil, status: nil, response_body: nil)
111
+ @status = status
112
+ @response_body = response_body
113
+ super(message || "Speech API error (status: #{status})")
114
+ end
115
+ end
91
116
  end
92
117
  end
@@ -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
72
+ begin
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
66
87
  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
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
@@ -285,7 +283,7 @@ module RubyLLM
285
283
  execution.create_detail!(detail_data) if has_data
286
284
 
287
285
  execution
288
- rescue StandardError => e
286
+ rescue => e
289
287
  # Log error but don't fail the agent execution itself
290
288
  Rails.logger.error("[RubyLLM::Agents] Failed to create execution record: #{e.message}")
291
289
  nil
@@ -356,7 +354,7 @@ module RubyLLM
356
354
  begin
357
355
  execution.calculate_costs!
358
356
  execution.save!
359
- rescue StandardError => cost_error
357
+ rescue => cost_error
360
358
  Rails.logger.warn("[RubyLLM::Agents] Cost calculation failed: #{cost_error.message}")
361
359
  end
362
360
  end
@@ -364,12 +362,12 @@ module RubyLLM
364
362
  # Record token usage for budget tracking
365
363
  record_token_usage(execution)
366
364
  rescue ActiveRecord::RecordInvalid => e
367
- 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(", ")}")
368
366
  if Rails.env.development? || Rails.env.test?
369
367
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
370
368
  end
371
369
  raise
372
- rescue StandardError => e
370
+ rescue => e
373
371
  Rails.logger.error("[RubyLLM::Agents] Failed to update execution record #{execution&.id}: #{e.class}: #{e.message}")
374
372
  if Rails.env.development? || Rails.env.test?
375
373
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
@@ -465,7 +463,7 @@ module RubyLLM
465
463
  begin
466
464
  execution.aggregate_attempt_costs!
467
465
  execution.save!
468
- rescue StandardError => cost_error
466
+ rescue => cost_error
469
467
  Rails.logger.warn("[RubyLLM::Agents] Cost calculation failed: #{cost_error.message}")
470
468
  end
471
469
  end
@@ -473,12 +471,12 @@ module RubyLLM
473
471
  # Record token usage for budget tracking
474
472
  record_token_usage(execution)
475
473
  rescue ActiveRecord::RecordInvalid => e
476
- 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(", ")}")
477
475
  if Rails.env.development? || Rails.env.test?
478
476
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
479
477
  end
480
478
  raise
481
- rescue StandardError => e
479
+ rescue => e
482
480
  Rails.logger.error("[RubyLLM::Agents] Failed to update execution record #{execution&.id}: #{e.class}: #{e.message}")
483
481
  if Rails.env.development? || Rails.env.test?
484
482
  Rails.logger.error("[RubyLLM::Agents] Update data: #{update_data.inspect}")
@@ -624,7 +622,7 @@ module RubyLLM
624
622
  # @return [String, nil] The system prompt or nil if unavailable
625
623
  def safe_system_prompt
626
624
  respond_to?(:system_prompt) ? system_prompt.to_s : nil
627
- rescue StandardError => e
625
+ rescue => e
628
626
  Rails.logger.warn("[RubyLLM::Agents] Could not capture system_prompt: #{e.message}")
629
627
  nil
630
628
  end
@@ -634,7 +632,7 @@ module RubyLLM
634
632
  # @return [String, nil] The user prompt or nil if unavailable
635
633
  def safe_user_prompt
636
634
  respond_to?(:user_prompt) ? user_prompt.to_s : nil
637
- rescue StandardError => e
635
+ rescue => e
638
636
  Rails.logger.warn("[RubyLLM::Agents] Could not capture user_prompt: #{e.message}")
639
637
  nil
640
638
  end
@@ -644,7 +642,7 @@ module RubyLLM
644
642
  # @return [String, nil] The assistant prompt or nil if unavailable
645
643
  def safe_assistant_prompt
646
644
  respond_to?(:assistant_prompt) ? assistant_prompt&.to_s : nil
647
- rescue StandardError => e
645
+ rescue => e
648
646
  Rails.logger.warn("[RubyLLM::Agents] Could not capture assistant_prompt: #{e.message}")
649
647
  nil
650
648
  end
@@ -665,7 +663,7 @@ module RubyLLM
665
663
  def safe_response_value(response, method, default = nil)
666
664
  return default unless response.respond_to?(method)
667
665
  response.public_send(method)
668
- rescue StandardError
666
+ rescue
669
667
  default
670
668
  end
671
669
 
@@ -702,7 +700,7 @@ module RubyLLM
702
700
  # @return [String, nil] Normalized finish reason
703
701
  def safe_extract_finish_reason(response)
704
702
  reason = safe_response_value(response, :finish_reason) ||
705
- safe_response_value(response, :stop_reason)
703
+ safe_response_value(response, :stop_reason)
706
704
  return nil unless reason
707
705
 
708
706
  # Normalize to standard values
@@ -850,7 +848,7 @@ module RubyLLM
850
848
  if tool_call.respond_to?(:to_h)
851
849
  tool_call.to_h
852
850
  else
853
- { id: id, name: tool_call[:name], arguments: tool_call[:arguments] }
851
+ {id: id, name: tool_call[:name], arguments: tool_call[:arguments]}
854
852
  end
855
853
  end
856
854
  end
@@ -919,7 +917,7 @@ module RubyLLM
919
917
  }
920
918
  execution.create_detail!(detail_data) if detail_data.values.any?(&:present?)
921
919
  end
922
- rescue StandardError => e
920
+ rescue => e
923
921
  Rails.logger.error("[RubyLLM::Agents] Failed to record cache hit execution: #{e.message}")
924
922
  end
925
923
 
@@ -940,7 +938,7 @@ module RubyLLM
940
938
  tenant_id: tenant_id,
941
939
  tenant_config: tenant_config
942
940
  )
943
- rescue StandardError => e
941
+ rescue => e
944
942
  Rails.logger.warn("[RubyLLM::Agents] Failed to record token usage: #{e.message}")
945
943
  end
946
944
  end
@@ -956,7 +954,7 @@ module RubyLLM
956
954
 
957
955
  @_assistant_prompt_column_exists = begin
958
956
  RubyLLM::Agents::ExecutionDetail.column_names.include?("assistant_prompt")
959
- rescue StandardError
957
+ rescue
960
958
  false
961
959
  end
962
960
  end
@@ -1001,16 +999,16 @@ module RubyLLM
1001
999
 
1002
1000
  # Store error_message in detail table (best-effort)
1003
1001
  begin
1004
- detail_attrs = { error_message: error_message.to_s.truncate(65535) }
1002
+ detail_attrs = {error_message: error_message.to_s.truncate(65535)}
1005
1003
  if execution.detail
1006
1004
  execution.detail.update_columns(detail_attrs)
1007
1005
  else
1008
1006
  RubyLLM::Agents::ExecutionDetail.create!(detail_attrs.merge(execution_id: execution.id))
1009
1007
  end
1010
- rescue StandardError
1008
+ rescue
1011
1009
  # Non-critical — error_class on execution is sufficient for filtering
1012
1010
  end
1013
- rescue StandardError => e
1011
+ rescue => e
1014
1012
  Rails.logger.error("[RubyLLM::Agents] CRITICAL: Failed emergency status update for execution #{execution&.id}: #{e.message}")
1015
1013
  end
1016
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.1.0"
7
+ VERSION = "3.3.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