Class: RubyCode::AgentLoop

Inherits:
Object
  • Object
show all
Defined in:
lib/rubycode/agent_loop.rb

Overview

Manages the agent loop - iterates until task completion or limits reached

Constant Summary collapse

MAX_ITERATIONS =
25
MAX_TOOL_CALLS =
50
MAX_CONSECUTIVE_RATE_LIMIT_ERRORS =
3

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(adapter:, memory:, config:, system_prompt:, options: {}) ⇒ AgentLoop

Returns a new instance of AgentLoop.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/rubycode/agent_loop.rb', line 16

def initialize(adapter:, memory:, config:, system_prompt:, options: {})
  @adapter = adapter
  @memory = memory
  @config = config
  @system_prompt = system_prompt
  @read_files = options[:read_files]
  @tty_prompt = options[:tty_prompt]
  @options = options
  @response_handler = Client::ResponseHandler.new(memory: @memory, config: @config)
  @display_formatter = Client::DisplayFormatter.new(config: @config)
  @approval_handler = options[:approval_handler] ||
                      Client::ApprovalHandler.new(tty_prompt: @tty_prompt, config: @config)
  @consecutive_rate_limit_errors = 0
end

Instance Attribute Details

#approval_handler ⇒ Object (readonly)

Returns the value of attribute approval_handler.



14
15
16
# File 'lib/rubycode/agent_loop.rb', line 14

def approval_handler
  @approval_handler
end

Instance Method Details

#build_retry_exhausted_message(error) ⇒ Object



70
71
72
73
74
75
76
77
# File 'lib/rubycode/agent_loop.rb', line 70

def build_retry_exhausted_message(error)
  "\n❌ Unable to reach LLM server after multiple retries.\n\n" \
    "Error: #{error.message}\n\n" \
    "Please check:\n  " \
    "• Is your LLM server running?\n  " \
    "• Are you being rate limited? (wait a few minutes)\n  " \
    "• Is the server URL correct in your config?\n"
end

#handle_empty_tool_calls_case(content, tool_calls, iteration, total_tool_calls) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/rubycode/agent_loop.rb', line 55

def handle_empty_tool_calls_case(content, tool_calls, iteration, total_tool_calls)
  return false unless tool_calls.empty?

  # Skip empty tool call handling if last response was an adapter error
  if @last_response_was_error
    @last_response_was_error = false # Reset flag
    return true # Continue loop
  end

  result = @response_handler.handle_empty_tool_calls(content, iteration, total_tool_calls)
  return result if result

  false
end

#run ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rubycode/agent_loop.rb', line 31

def run
  iteration = 0
  total_tool_calls = 0
  @last_response_was_error = false

  loop do
    iteration += 1

    return @response_handler.handle_max_iterations(iteration) if iteration > MAX_ITERATIONS

    content, tool_calls = llm_response

    next if handle_empty_tool_calls_case(content, tool_calls, iteration, total_tool_calls)

    total_tool_calls += tool_calls.length
    return @response_handler.handle_max_tool_calls(content, total_tool_calls) if total_tool_calls > MAX_TOOL_CALLS

    done_result = execute_tool_calls(tool_calls, iteration)
    return @response_handler.finalize_response(done_result, iteration, total_tool_calls) if done_result
  end
rescue RubyCode::AdapterRetryExhaustedError => e
  build_retry_exhausted_message(e)
end