Class: RubyRich::AgentShell

Inherits:
AppShell show all
Defined in:
lib/ruby_rich/agent_shell.rb

Constant Summary

Constants inherited from AppShell

RubyRich::AppShell::DEFAULT_COMMANDS

Instance Attribute Summary collapse

Attributes inherited from AppShell

#composer, #focus_manager, #layout, #live, #progress_manager, #sidebar, #theme, #token_usage, #transcript, #viewport

Instance Method Summary collapse

Methods inherited from AppShell

#add_assistant, #add_separator, #add_thinking, #add_tool, #add_user, #confirm, #form, #open_pager, #set_tasks, #start_progress, #status=, #update_plan, #with_progress

Constructor Details

#initialize(**options) ⇒ AgentShell

Returns a new instance of AgentShell.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/ruby_rich/agent_shell.rb', line 9

def initialize(**options)
  @callbacks = {}
  @state_mutex = Mutex.new
  @id_mutex = Mutex.new
  @pending_actions = Queue.new
  @entry_sequence = 0
  @state = :initialized
  @ui_thread = nil
  @mode = :chat
  super
  @stop_on_ctrl_c = false
  @composer.instance_variable_set(:@on_interrupt, method(:handle_interrupt))
  @composer.instance_variable_set(:@on_eof, method(:handle_eof))
  attach_agent_controls
end

Instance Attribute Details

#modeObject (readonly)

Returns the value of attribute mode.



7
8
9
# File 'lib/ruby_rich/agent_shell.rb', line 7

def mode
  @mode
end

Instance Method Details

#add_assistant_message(text, streaming: false) ⇒ Object



85
86
87
# File 'lib/ruby_rich/agent_shell.rb', line 85

def add_assistant_message(text, streaming: false)
  add_message(:assistant, text, streaming: streaming)
end

#add_diff(title: nil, content:, language: "diff") ⇒ Object



101
102
103
104
# File 'lib/ruby_rich/agent_shell.rb', line 101

def add_diff(title: nil, content:, language: "diff")
  text = title ? "#{title}\n#{content}" : content
  add_message(:diff, text, language: language)
end

#add_error_message(text) ⇒ Object



97
98
99
# File 'lib/ruby_rich/agent_shell.rb', line 97

def add_error_message(text)
  add_message(:error, text)
end

#add_markdown(content, streaming: false) ⇒ Object



89
90
91
# File 'lib/ruby_rich/agent_shell.rb', line 89

def add_markdown(content, streaming: false)
  add_message(:markdown, content, streaming: streaming)
end

#add_system_message(text) ⇒ Object



93
94
95
# File 'lib/ruby_rich/agent_shell.rb', line 93

def add_system_message(text)
  add_message(:system, text)
end

#add_user_message(text) ⇒ Object



81
82
83
# File 'lib/ruby_rich/agent_shell.rb', line 81

def add_user_message(text)
  add_message(:user, text)
end

#append_to_message(id, delta) ⇒ Object



106
107
108
# File 'lib/ruby_rich/agent_shell.rb', line 106

def append_to_message(id, delta)
  dispatch { @transcript.append_block(id, delta).tap { @viewport.scroll_to_bottom } }
end

#finish_tool_call(id, status: :done, output: nil) ⇒ Object



143
144
145
# File 'lib/ruby_rich/agent_shell.rb', line 143

def finish_tool_call(id, status: :done, output: nil)
  update_tool_call(id, status: status, output: output)
end

#on_command(&block) ⇒ Object



40
41
42
43
# File 'lib/ruby_rich/agent_shell.rb', line 40

def on_command(&block)
  @callbacks[:command] = block
  self
end

#on_interrupt(&block) ⇒ Object



30
31
32
33
# File 'lib/ruby_rich/agent_shell.rb', line 30

def on_interrupt(&block)
  @callbacks[:interrupt] = block
  self
end

#on_mode_toggle(&block) ⇒ Object



35
36
37
38
# File 'lib/ruby_rich/agent_shell.rb', line 35

def on_mode_toggle(&block)
  @callbacks[:mode_toggle] = block
  self
end

#on_submit(&block) ⇒ Object



25
26
27
28
# File 'lib/ruby_rich/agent_shell.rb', line 25

def on_submit(&block)
  @callbacks[:submit] = block
  self
end

#remove_entry(id) ⇒ Object



114
115
116
# File 'lib/ruby_rich/agent_shell.rb', line 114

def remove_entry(id)
  dispatch { @transcript.remove_block(id).tap { @viewport.scroll_to_bottom } }
end

#replace_message(id, text) ⇒ Object



110
111
112
# File 'lib/ruby_rich/agent_shell.rb', line 110

def replace_message(id, text)
  dispatch { @transcript.replace_block(id, text).tap { @viewport.scroll_to_bottom } }
end

#show_token_usage(input: nil, output: nil, total: nil, **extra) ⇒ Object



155
156
157
# File 'lib/ruby_rich/agent_shell.rb', line 155

def show_token_usage(input: nil, output: nil, total: nil, **extra)
  dispatch { @token_usage = { input: input, output: output, total: total }.merge(extra).compact }
end

#start(refresh_rate: 24, mouse: true, alt_screen: false) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ruby_rich/agent_shell.rb', line 45

def start(refresh_rate: 24, mouse: true, alt_screen: false)
  @state_mutex.synchronize { @state = :starting }
  Live.start(@layout, refresh_rate: refresh_rate, mouse: mouse, alt_screen: alt_screen, autowrap: false) do |live|
    @state_mutex.synchronize do
      @live = live
      @ui_thread = Thread.current
      @state = :running
    end
    drain_pending_actions(live)
    live.listening = true
  end
ensure
  @state_mutex.synchronize do
    @live = nil
    @ui_thread = nil
    @state = :stopped
  end
end

#start_tool_call(name:, input: nil, status: :running) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
# File 'lib/ruby_rich/agent_shell.rb', line 118

def start_tool_call(name:, input: nil, status: :running)
  id = reserve_id(:tool)
  return nil unless id

  ok = dispatch do
    @transcript.add_tool(name, status: status, result: tool_body(input: input), collapsed: false, id: id)
    @viewport.scroll_to_bottom
    id
  end
  ok ? id : nil
end

#stopObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ruby_rich/agent_shell.rb', line 64

def stop
  dispatch_after_start_failure = false
  @state_mutex.synchronize do
    return false if @state == :stopped

    if @live
      return @live.stop if Thread.current == @ui_thread

      return @live.post { |live| live.stop } || false
    end

    @state = :stopped
    dispatch_after_start_failure = true
  end
  dispatch_after_start_failure
end

#stopped?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/ruby_rich/agent_shell.rb', line 159

def stopped?
  @state_mutex.synchronize { @state == :stopped }
end

#update_status(text) ⇒ Object



151
152
153
# File 'lib/ruby_rich/agent_shell.rb', line 151

def update_status(text)
  dispatch { @status = text.to_s }
end

#update_tasks(tasks) ⇒ Object



147
148
149
# File 'lib/ruby_rich/agent_shell.rb', line 147

def update_tasks(tasks)
  dispatch { @sidebar.set_tasks(tasks) }
end

#update_tool_call(id, status: nil, output: nil, input: nil) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/ruby_rich/agent_shell.rb', line 130

def update_tool_call(id, status: nil, output: nil, input: nil)
  options = {}
  options[:status] = status if status
  text = output.nil? && input.nil? ? nil : tool_body(input: input, output: output)
  dispatch do
    block = @transcript.find_block(id)
    next false unless block

    text ||= block[:text]
    @transcript.replace_block(id, text, **options).tap { @viewport.scroll_to_bottom }
  end
end