Class: DSPy::LM::RetryHandler

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/dspy/lm/retry_handler.rb

Overview

Handles retry logic with progressive fallback strategies

Constant Summary collapse

MAX_RETRIES =
3
BACKOFF_BASE =

seconds

0.5

Instance Method Summary collapse

Constructor Details

#initialize(adapter, signature_class) ⇒ RetryHandler

Returns a new instance of RetryHandler.



16
17
18
19
20
# File 'lib/dspy/lm/retry_handler.rb', line 16

def initialize(adapter, signature_class)
  @adapter = adapter
  @signature_class = signature_class
  @attempt = 0
end

Instance Method Details

#with_retry(initial_strategy, &block) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/dspy/lm/retry_handler.rb', line 31

def with_retry(initial_strategy, &block)
  # Skip retries entirely if disabled
  unless DSPy.config.structured_outputs.retry_enabled
    return yield(initial_strategy)
  end
  
  strategies = build_fallback_chain(initial_strategy)
  last_error = nil

  strategies.each do |strategy|
    retry_count = 0
    
    begin
      @attempt += 1
      DSPy.logger.debug("Attempting with strategy: #{strategy.name} (attempt #{@attempt})")
      
      result = yield(strategy)
      
      # Success! Reset attempt counter for next time
      @attempt = 0
      return result
      
    rescue JSON::ParserError, StandardError => e
      last_error = e
      
      # Let strategy handle the error first
      if strategy.handle_error(e)
        DSPy.logger.info("Strategy #{strategy.name} handled error, will try next strategy")
        next # Try next strategy
      end
      
      # Try retrying with the same strategy
      if retry_count < max_retries_for_strategy(strategy)
        retry_count += 1
        backoff_time = calculate_backoff(retry_count)
        
        DSPy.logger.warn(
          "Retrying #{strategy.name} after error (attempt #{retry_count}/#{max_retries_for_strategy(strategy)}): #{e.message}"
        )
        
        Async::Task.current.sleep(backoff_time) if backoff_time > 0
        retry
      else
        DSPy.logger.info("Max retries reached for #{strategy.name}, trying next strategy")
        next # Try next strategy
      end
    end
  end

  # All strategies exhausted
  DSPy.logger.error("All strategies exhausted after #{@attempt} total attempts")
  raise last_error || StandardError.new("All JSON extraction strategies failed")
end