ruby_llm-agents 3.5.4 → 3.6.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +155 -10
  4. data/app/helpers/ruby_llm/agents/application_helper.rb +15 -1
  5. data/app/models/ruby_llm/agents/execution/replayable.rb +124 -0
  6. data/app/models/ruby_llm/agents/execution/scopes.rb +42 -1
  7. data/app/models/ruby_llm/agents/execution.rb +50 -1
  8. data/app/models/ruby_llm/agents/tenant/budgetable.rb +28 -4
  9. data/app/views/layouts/ruby_llm/agents/application.html.erb +41 -28
  10. data/app/views/ruby_llm/agents/agents/show.html.erb +16 -1
  11. data/app/views/ruby_llm/agents/dashboard/_top_tenants.html.erb +47 -0
  12. data/app/views/ruby_llm/agents/dashboard/index.html.erb +397 -100
  13. data/lib/generators/ruby_llm_agents/rename_agent_generator.rb +53 -0
  14. data/lib/generators/ruby_llm_agents/templates/rename_agent_migration.rb.tt +19 -0
  15. data/lib/ruby_llm/agents/agent_tool.rb +125 -0
  16. data/lib/ruby_llm/agents/audio/speaker.rb +5 -3
  17. data/lib/ruby_llm/agents/audio/speech_pricing.rb +63 -187
  18. data/lib/ruby_llm/agents/audio/transcriber.rb +5 -3
  19. data/lib/ruby_llm/agents/audio/transcription_pricing.rb +5 -7
  20. data/lib/ruby_llm/agents/base_agent.rb +144 -5
  21. data/lib/ruby_llm/agents/core/configuration.rb +178 -53
  22. data/lib/ruby_llm/agents/core/errors.rb +3 -77
  23. data/lib/ruby_llm/agents/core/instrumentation.rb +0 -17
  24. data/lib/ruby_llm/agents/core/version.rb +1 -1
  25. data/lib/ruby_llm/agents/dsl/base.rb +0 -8
  26. data/lib/ruby_llm/agents/dsl/queryable.rb +124 -0
  27. data/lib/ruby_llm/agents/dsl.rb +1 -0
  28. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -1
  29. data/lib/ruby_llm/agents/image/generator/pricing.rb +75 -217
  30. data/lib/ruby_llm/agents/image/generator.rb +5 -3
  31. data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +8 -0
  32. data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +4 -2
  33. data/lib/ruby_llm/agents/pipeline/builder.rb +43 -0
  34. data/lib/ruby_llm/agents/pipeline/context.rb +11 -1
  35. data/lib/ruby_llm/agents/pipeline/executor.rb +1 -25
  36. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +26 -1
  37. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +18 -0
  38. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +130 -3
  39. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +29 -0
  40. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +11 -4
  41. data/lib/ruby_llm/agents/pipeline.rb +0 -92
  42. data/lib/ruby_llm/agents/results/background_removal_result.rb +11 -1
  43. data/lib/ruby_llm/agents/results/base.rb +23 -1
  44. data/lib/ruby_llm/agents/results/embedding_result.rb +14 -1
  45. data/lib/ruby_llm/agents/results/image_analysis_result.rb +11 -1
  46. data/lib/ruby_llm/agents/results/image_edit_result.rb +11 -1
  47. data/lib/ruby_llm/agents/results/image_generation_result.rb +12 -3
  48. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +11 -1
  49. data/lib/ruby_llm/agents/results/image_transform_result.rb +11 -1
  50. data/lib/ruby_llm/agents/results/image_upscale_result.rb +11 -1
  51. data/lib/ruby_llm/agents/results/image_variation_result.rb +11 -1
  52. data/lib/ruby_llm/agents/results/speech_result.rb +20 -1
  53. data/lib/ruby_llm/agents/results/transcription_result.rb +20 -1
  54. data/lib/ruby_llm/agents/text/embedder.rb +23 -18
  55. data/lib/ruby_llm/agents.rb +70 -5
  56. data/lib/tasks/ruby_llm_agents.rake +21 -0
  57. metadata +7 -6
  58. data/lib/ruby_llm/agents/infrastructure/reliability/breaker_manager.rb +0 -80
  59. data/lib/ruby_llm/agents/infrastructure/reliability/execution_constraints.rb +0 -69
  60. data/lib/ruby_llm/agents/infrastructure/reliability/executor.rb +0 -125
  61. data/lib/ruby_llm/agents/infrastructure/reliability/fallback_routing.rb +0 -72
  62. data/lib/ruby_llm/agents/infrastructure/reliability/retry_strategy.rb +0 -82
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Agents
5
- module Reliability
6
- # Coordinates reliability features during agent execution
7
- #
8
- # Orchestrates retry strategy, fallback routing, circuit breakers,
9
- # and execution constraints into a cohesive execution flow.
10
- #
11
- # @example
12
- # executor = Executor.new(
13
- # config: { retries: { max: 3 }, fallback_models: ["gpt-4o-mini"] },
14
- # primary_model: "gpt-4o",
15
- # agent_type: "MyAgent"
16
- # )
17
- # executor.execute { |model| call_llm(model) }
18
- #
19
- # @api private
20
- class Executor
21
- attr_reader :retry_strategy, :fallback_routing, :breaker_manager, :constraints
22
-
23
- # @param config [Hash] Reliability configuration
24
- # @param primary_model [String] Primary model identifier
25
- # @param agent_type [String] Agent class name
26
- # @param tenant_id [String, nil] Optional tenant identifier
27
- def initialize(config:, primary_model:, agent_type:, tenant_id: nil)
28
- retries_config = config[:retries] || {}
29
-
30
- @retry_strategy = RetryStrategy.new(
31
- max: retries_config[:max] || 0,
32
- backoff: retries_config[:backoff] || :exponential,
33
- base: retries_config[:base] || 0.4,
34
- max_delay: retries_config[:max_delay] || 3.0,
35
- on: retries_config[:on] || [],
36
- patterns: config[:retryable_patterns]
37
- )
38
-
39
- @fallback_routing = FallbackRouting.new(
40
- primary_model,
41
- fallback_models: config[:fallback_models] || []
42
- )
43
-
44
- @breaker_manager = BreakerManager.new(
45
- agent_type,
46
- config: config[:circuit_breaker],
47
- tenant_id: tenant_id
48
- )
49
-
50
- @constraints = ExecutionConstraints.new(
51
- total_timeout: config[:total_timeout]
52
- )
53
-
54
- @last_error = nil
55
- end
56
-
57
- # Returns all models that will be tried
58
- #
59
- # @return [Array<String>] Model identifiers
60
- def models_to_try
61
- fallback_routing.models
62
- end
63
-
64
- # Executes with full reliability support
65
- #
66
- # Iterates through models with retries, respecting circuit breakers
67
- # and timeout constraints.
68
- #
69
- # @yield [model] Block to execute with the current model
70
- # @yieldparam model [String] The model to use for this attempt
71
- # @return [Object] Result of successful execution
72
- # @raise [AllModelsExhaustedError] If all models fail
73
- # @raise [TotalTimeoutError] If total timeout exceeded
74
- def execute
75
- until fallback_routing.exhausted?
76
- model = fallback_routing.current_model
77
-
78
- # Check circuit breaker
79
- if breaker_manager.open?(model)
80
- fallback_routing.advance!
81
- next
82
- end
83
-
84
- # Try with retries
85
- result = execute_with_retries(model) { |m| yield(m) }
86
- return result if result
87
-
88
- fallback_routing.advance!
89
- end
90
-
91
- raise AllModelsExhaustedError.new(
92
- fallback_routing.models,
93
- @last_error || StandardError.new("All models failed")
94
- )
95
- end
96
-
97
- private
98
-
99
- def execute_with_retries(model)
100
- attempt_index = 0
101
-
102
- loop do
103
- constraints.enforce_timeout!
104
-
105
- begin
106
- result = yield(model)
107
- breaker_manager.record_success!(model)
108
- return result
109
- rescue => e
110
- @last_error = e
111
- breaker_manager.record_failure!(model)
112
-
113
- if retry_strategy.retryable?(e) && retry_strategy.should_retry?(attempt_index)
114
- attempt_index += 1
115
- sleep(retry_strategy.delay_for(attempt_index))
116
- else
117
- return nil # Move to next model
118
- end
119
- end
120
- end
121
- end
122
- end
123
- end
124
- end
125
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Agents
5
- module Reliability
6
- # Routes execution through fallback models when primary fails
7
- #
8
- # Manages the model fallback chain and tracks which models have been tried.
9
- #
10
- # @example
11
- # routing = FallbackRouting.new("gpt-4o", fallback_models: ["gpt-4o-mini"])
12
- # routing.current_model # => "gpt-4o"
13
- # routing.advance! # => "gpt-4o-mini"
14
- # routing.exhausted? # => false
15
- #
16
- # @api private
17
- class FallbackRouting
18
- attr_reader :models
19
-
20
- # @param primary_model [String] The primary model identifier
21
- # @param fallback_models [Array<String>] Fallback model identifiers
22
- def initialize(primary_model, fallback_models: [])
23
- @models = [primary_model, *fallback_models].uniq
24
- @current_index = 0
25
- end
26
-
27
- # Returns the current model to try
28
- #
29
- # @return [String, nil] Model identifier or nil if exhausted
30
- def current_model
31
- models[@current_index]
32
- end
33
-
34
- # Advances to the next fallback model
35
- #
36
- # @return [String, nil] Next model or nil if exhausted
37
- def advance!
38
- @current_index += 1
39
- current_model
40
- end
41
-
42
- # Checks if more models are available after current
43
- #
44
- # @return [Boolean] true if more models to try
45
- def has_more?
46
- @current_index < models.length - 1
47
- end
48
-
49
- # Checks if all models have been exhausted
50
- #
51
- # @return [Boolean] true if no more models
52
- def exhausted?
53
- @current_index >= models.length
54
- end
55
-
56
- # Resets to the first model
57
- #
58
- # @return [void]
59
- def reset!
60
- @current_index = 0
61
- end
62
-
63
- # Returns models that have been tried so far
64
- #
65
- # @return [Array<String>] Models already attempted
66
- def tried_models
67
- models[0..@current_index]
68
- end
69
- end
70
- end
71
- end
72
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Agents
5
- module Reliability
6
- # Handles retry logic with configurable backoff strategies
7
- #
8
- # Provides exponential and constant backoff with jitter,
9
- # retry counting, and delay calculation.
10
- #
11
- # @example
12
- # strategy = RetryStrategy.new(max: 3, backoff: :exponential, base: 0.4)
13
- # strategy.should_retry?(attempt_index) # => true/false
14
- # strategy.delay_for(attempt_index) # => 0.6 (with jitter)
15
- #
16
- # @api private
17
- class RetryStrategy
18
- attr_reader :max, :backoff, :base, :max_delay, :custom_errors, :custom_patterns
19
-
20
- # @param max [Integer] Maximum retry attempts
21
- # @param backoff [Symbol] :constant or :exponential
22
- # @param base [Float] Base delay in seconds
23
- # @param max_delay [Float] Maximum delay cap
24
- # @param on [Array<Class>] Additional error classes to retry on
25
- # @param patterns [Array<String>, nil] Additional patterns to match in error messages
26
- def initialize(max: 0, backoff: :exponential, base: 0.4, max_delay: 3.0, on: [], patterns: nil)
27
- @max = max
28
- @backoff = backoff
29
- @base = base
30
- @max_delay = max_delay
31
- @custom_errors = Array(on)
32
- @custom_patterns = patterns
33
- end
34
-
35
- # Determines if retry should occur
36
- #
37
- # @param attempt_index [Integer] Current attempt number (0-indexed)
38
- # @return [Boolean] true if should retry
39
- def should_retry?(attempt_index)
40
- attempt_index < max
41
- end
42
-
43
- # Calculates delay before next retry
44
- #
45
- # @param attempt_index [Integer] Current attempt number
46
- # @return [Float] Delay in seconds (includes jitter)
47
- def delay_for(attempt_index)
48
- base_delay = case backoff
49
- when :constant
50
- base
51
- when :exponential
52
- [base * (2**attempt_index), max_delay].min
53
- else
54
- base
55
- end
56
-
57
- # Add jitter (0-50% of base delay)
58
- base_delay + (rand * base_delay * 0.5)
59
- end
60
-
61
- # Checks if an error is retryable
62
- #
63
- # @param error [Exception] The error to check
64
- # @return [Boolean] true if retryable
65
- def retryable?(error)
66
- RubyLLM::Agents::Reliability.retryable_error?(
67
- error,
68
- custom_errors: custom_errors,
69
- custom_patterns: custom_patterns
70
- )
71
- end
72
-
73
- # Returns all retryable error classes
74
- #
75
- # @return [Array<Class>] Error classes to retry on
76
- def retryable_errors
77
- RubyLLM::Agents::Reliability.default_retryable_errors + custom_errors
78
- end
79
- end
80
- end
81
- end
82
- end