Class: AIA::UIPresenter

Inherits:
Object
  • Object
show all
Defined in:
lib/aia/ui_presenter.rb

Constant Summary collapse

USER_PROMPT =
"Follow up (cntl-D or 'exit' to end) #=> "

Instance Method Summary collapse

Constructor Details

#initializeUIPresenter

Returns a new instance of UIPresenter.



11
12
13
# File 'lib/aia/ui_presenter.rb', line 11

def initialize
  @terminal_width = TTY::Screen.width
end

Instance Method Details

#ask_questionObject

This is the follow up question in a chat session



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/aia/ui_presenter.rb', line 86

def ask_question
  puts USER_PROMPT
  $stdout.flush  # Ensure the prompt is displayed immediately
  begin
    input = Reline.readline('', true)
    return nil if input.nil? # Handle Ctrl+D
    Reline::HISTORY << input unless input.strip.empty?
    input
  rescue Interrupt
    puts "\nChat session interrupted."
    return 'exit'
  end
end

#display_ai_response(response) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
# File 'lib/aia/ui_presenter.rb', line 25

def display_ai_response(response)
  puts "\nAI: "
  format_chat_response(response)

  if AIA.config.out_file && !AIA.config.out_file.nil?
    File.open(AIA.config.out_file, 'a') do |file|
      file.puts "\nAI: "
      format_chat_response(response, file)
    end
  end
end

#display_chat_endObject



80
81
82
# File 'lib/aia/ui_presenter.rb', line 80

def display_chat_end
  puts "\nChat session ended."
end

#display_chat_headerObject



15
16
17
# File 'lib/aia/ui_presenter.rb', line 15

def display_chat_header
  puts "#{'═' * @terminal_width}\n"
end

#display_info(message) ⇒ Object



100
101
102
# File 'lib/aia/ui_presenter.rb', line 100

def display_info(message)
  puts "\n#{message}"
end

#display_multi_model_metrics(metrics_list) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/aia/ui_presenter.rb', line 147

def display_multi_model_metrics(metrics_list)
  return unless metrics_list && !metrics_list.empty?
  
  output_lines = []
  
  # Determine table width based on whether costs are shown
  if AIA.config.show_cost
    table_width = 80
  else
    table_width = 60
  end
  
  output_lines << "═" * table_width
  output_lines << "Multi-Model Token Usage"
  output_lines << "─" * table_width
  
  # Build header row
  if AIA.config.show_cost
    output_lines << sprintf("%-20s %10s %10s %10s %12s %10s", 
                            "Model", "Input", "Output", "Total", "Cost", "x1000")
    output_lines << "─" * table_width
  else
    output_lines << sprintf("%-20s %10s %10s %10s", 
                            "Model", "Input", "Output", "Total")
    output_lines << "─" * table_width
  end
  
  # Process each model
  total_input = 0
  total_output = 0
  total_cost = 0.0
  
  metrics_list.each do |metrics|
    model_name = metrics[:model_id]
    # Truncate model name if too long
    model_name = model_name[0..17] + ".." if model_name.length > 19
    
    input_tokens = metrics[:input_tokens] || 0
    output_tokens = metrics[:output_tokens] || 0
    total_tokens = input_tokens + output_tokens
    
    if AIA.config.show_cost
      cost_data = calculate_cost(metrics)
      if cost_data[:available]
        cost_str = "$#{'%.5f' % cost_data[:total_cost]}"
        x1000_str = "$#{'%.2f' % (cost_data[:total_cost] * 1000)}"
        total_cost += cost_data[:total_cost]
      else
        cost_str = "N/A"
        x1000_str = "N/A"
      end
      
      output_lines << sprintf("%-20s %10d %10d %10d %12s %10s",
                              model_name, input_tokens, output_tokens, total_tokens, cost_str, x1000_str)
    else
      output_lines << sprintf("%-20s %10d %10d %10d",
                              model_name, input_tokens, output_tokens, total_tokens)
    end
    
    total_input += input_tokens
    total_output += output_tokens
  end
  
  # Display totals row
  output_lines << "─" * table_width
  total_tokens = total_input + total_output
  
  if AIA.config.show_cost && total_cost > 0
    cost_str = "$#{'%.5f' % total_cost}"
    x1000_str = "$#{'%.2f' % (total_cost * 1000)}"
    output_lines << sprintf("%-20s %10d %10d %10d %12s %10s",
                           "TOTAL", total_input, total_output, total_tokens, cost_str, x1000_str)
  else
    output_lines << sprintf("%-20s %10d %10d %10d",
                           "TOTAL", total_input, total_output, total_tokens)
  end
  
  output_lines << "═" * table_width
  
  # Output to STDOUT
  output_lines.each { |line| puts line }
  
  # Also write to file if configured
  if AIA.config.out_file && !AIA.config.out_file.nil?
    File.open(AIA.config.out_file, 'a') do |file|
      output_lines.each { |line| file.puts line }
    end
  end
end

#display_separatorObject



75
76
77
# File 'lib/aia/ui_presenter.rb', line 75

def display_separator
  puts "\n#{'─' * @terminal_width}"
end

#display_thinking_animationObject



20
21
22
# File 'lib/aia/ui_presenter.rb', line 20

def display_thinking_animation
  puts "\n⏳ Processing...\n"
end

#display_token_metrics(metrics) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/aia/ui_presenter.rb', line 121

def display_token_metrics(metrics)
  return unless metrics
  
  output_lines = []
  output_lines << "═" * 55
  output_lines << "Model: #{metrics[:model_id]}"
  
  if AIA.config.show_cost
    output_lines.concat(format_metrics_with_cost(metrics))
  else
    output_lines.concat(format_metrics_basic(metrics))
  end
  
  output_lines << "═" * 55
  
  # Output to STDOUT
  output_lines.each { |line| puts line }
  
  # Also write to file if configured
  if AIA.config.out_file && !AIA.config.out_file.nil?
    File.open(AIA.config.out_file, 'a') do |file|
      output_lines.each { |line| file.puts line }
    end
  end
end

#format_chat_response(response, output = $stdout) ⇒ Object



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
# File 'lib/aia/ui_presenter.rb', line 38

def format_chat_response(response, output = $stdout)
  indent = '   '

  # Convert RubyLLM::Message to string if necessary
  response_text = if response.is_a?(RubyLLM::Message)
                    response.content.to_s
                  elsif response.respond_to?(:to_s)
                    response.to_s
                  else
                    response
                  end

  in_code_block = false
  language = ''

  response_text.each_line do |line|
    line = line.chomp

    # Check for code block delimiters
    if (match = line.match(/^```(\w*)$/)) && !in_code_block
      in_code_block = true
      language = match[1]
      output.puts "#{indent}```#{language}"
    elsif line.match?(/^```$/) && in_code_block
      in_code_block = false
      output.puts "#{indent}```"
    elsif in_code_block
      # Print code with special formatting
      output.puts "#{indent}#{line}"
    else
      # Handle regular text
      output.puts "#{indent}#{line}"
    end
  end
end

#with_spinner(message = "Processing", operation_type = nil) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/aia/ui_presenter.rb', line 104

def with_spinner(message = "Processing", operation_type = nil)
  if AIA.verbose?
    spinner_message = operation_type ? "#{message} #{operation_type}..." : "#{message}..."
    spinner = TTY::Spinner.new("[:spinner] #{spinner_message}", format: :bouncing_ball)
    spinner.auto_spin

    begin
      result = yield
    ensure
      spinner.stop
    end
    result
  else
    yield
  end
end