Module: RubyLLM::Resilience

Defined in:
lib/ruby_llm/resilience.rb,
lib/ruby_llm/resilience/chain.rb,
lib/ruby_llm/resilience/engine.rb,
lib/ruby_llm/resilience/errors.rb,
lib/ruby_llm/resilience/breaker.rb,
lib/ruby_llm/resilience/version.rb,
lib/ruby_llm/resilience/tier_namer.rb,
lib/ruby_llm/resilience/memory_store.rb,
lib/ruby_llm/resilience/configuration.rb,
lib/generators/resilience/install_generator.rb,
app/controllers/ruby_llm/resilience/breakers_controller.rb

Overview

Circuit breakers and fallback chains for LLM apps.

RubyLLM::Resilience.configure do |c|
c.cache_store = ActiveSupport::Cache::RedisCacheStore.new(...)
c.fallback_models = { "claude-haiku-4-5" => "claude-sonnet-4-6" }
c.on_error  = ->(error, ctx) { Rails.error.report(error, context: ctx) }
c.on_status = ->(service, state) { Appsignal.set_gauge("circuit_breaker.state", state == :open ? 1 : 0, service:) }
end

RubyLLM::Resilience.run("api:openai:embeddings") { RubyLLM.embed(text) }

Defined Under Namespace

Modules: Chain, ErrorResolution, TierNamer Classes: Breaker, BreakerTripped, BreakersController, Configuration, Engine, InstallGenerator, MemoryStore

Constant Summary collapse

VERSION =
"0.6.0"

Class Method Summary collapse

Class Method Details

.configObject



28
29
30
# File 'lib/ruby_llm/resilience.rb', line 28

def config
  @config ||= Configuration.new
end

.configure {|@config| ... } ⇒ Object

Configure once at boot; the configuration freezes afterwards so fiber/thread safety comes from immutability. Use reset_configuration! in tests.

Yields:



35
36
37
38
39
40
# File 'lib/ruby_llm/resilience.rb', line 35

def configure
  @config ||= Configuration.new
  yield @config
  @config.freeze
  @config
end

.fallback_routesObject

Fallback routing grouped by breaker service, for dashboards:

{ "api:anthropic:haiku" => ["claude-haiku-4-5 → claude-sonnet-4-6"], ... }

A service can carry several routes when multiple models map into the same tier breaker.



74
75
76
77
78
79
80
# File 'lib/ruby_llm/resilience.rb', line 74

def fallback_routes
  config.fallback_models.keys.each_with_object(Hash.new { |h, k| h[k] = [] }) do |model, routes|
    service = service_for(model)
    chain = [ model, *config.fallbacks_for(model) ]
    routes[service] << chain.join("")
  end
end

.reset_configuration!Object



42
43
44
45
46
# File 'lib/ruby_llm/resilience.rb', line 42

def reset_configuration!
  @config = Configuration.new
  Breaker.reset_registry!
  @config
end

.run(service, &block) ⇒ Object



48
49
50
# File 'lib/ruby_llm/resilience.rb', line 48

def run(service, &block)
  Chain.run(service, &block)
end

.run_with_fallback(*steps) ⇒ Object



56
57
58
# File 'lib/ruby_llm/resilience.rb', line 56

def run_with_fallback(*steps)
  Chain.run_with_fallback(*steps)
end

.run_with_model_fallback(primary_model, fallback: :map, &block) ⇒ Object



52
53
54
# File 'lib/ruby_llm/resilience.rb', line 52

def run_with_model_fallback(primary_model, fallback: :map, &block)
  Chain.run_with_model_fallback(primary_model, fallback: fallback, &block)
end

.service_for(model_name) ⇒ Object

Convenience: the breaker name the configured service_namer would assign to a model.



66
67
68
# File 'lib/ruby_llm/resilience.rb', line 66

def service_for(model_name)
  config.service_namer.call(model_name)
end

.trippable?(error) ⇒ Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/ruby_llm/resilience.rb', line 60

def trippable?(error)
  config.resolved_trippable_errors.any? { |klass| error.is_a?(klass) }
end