| 
2 | 2 | # frozen_string_literal: true  | 
3 | 3 | 
 
  | 
4 | 4 | require "json"  | 
 | 5 | +require "readline"  | 
5 | 6 | require_relative "../../lib/agents"  | 
6 | 7 | require_relative "agents_factory"  | 
7 | 8 | 
 
  | 
@@ -29,68 +30,96 @@ def initialize  | 
29 | 30 |     @context = {}  | 
30 | 31 |     @current_status = ""  | 
31 | 32 | 
 
  | 
32 |  | -    puts "🏢 Welcome to ISP Customer Support!"  | 
33 |  | -    puts "Type '/help' for commands or 'exit' to quit."  | 
 | 33 | +    puts green("🏢 Welcome to ISP Customer Support!")  | 
 | 34 | +    puts dim_text("Type '/help' for commands or 'exit' to quit.")  | 
34 | 35 |     puts  | 
35 | 36 |   end  | 
36 | 37 | 
 
  | 
37 | 38 |   def start  | 
38 | 39 |     loop do  | 
39 |  | -      print "💬 You: "  | 
40 |  | -      user_input = gets.chomp.strip  | 
 | 40 | +      user_input = Readline.readline(cyan("\u{1F4AC} You: "), true)  | 
 | 41 | +      next unless user_input # Handle Ctrl+D  | 
41 | 42 | 
 
  | 
 | 43 | +      user_input = user_input.strip  | 
42 | 44 |       command_result = handle_command(user_input)  | 
43 | 45 |       break if command_result == :exit  | 
44 | 46 |       next if command_result == :handled || user_input.empty?  | 
45 | 47 | 
 
  | 
46 | 48 |       # Clear any previous status and show agent is working  | 
47 | 49 |       clear_status_line  | 
48 |  | -      print "🤖 Processing..."  | 
 | 50 | +      print yellow("🤖 Processing...")  | 
49 | 51 | 
 
  | 
50 |  | -      # Use the runner - it automatically determines the right agent from context  | 
51 |  | -      result = @runner.run(user_input, context: @context)  | 
 | 52 | +      begin  | 
 | 53 | +        # Use the runner - it automatically determines the right agent from context  | 
 | 54 | +        result = @runner.run(user_input, context: @context)  | 
52 | 55 | 
 
  | 
53 |  | -      # Update our context with the returned context from Runner  | 
54 |  | -      @context = result.context if result.respond_to?(:context) && result.context  | 
 | 56 | +        # Update our context with the returned context from Runner  | 
 | 57 | +        @context = result.context if result.respond_to?(:context) && result.context  | 
55 | 58 | 
 
  | 
56 |  | -      # Clear status and show response  | 
57 |  | -      clear_status_line  | 
 | 59 | +        # Clear status and show response with callback history  | 
 | 60 | +        clear_status_line  | 
58 | 61 | 
 
  | 
59 |  | -      # Handle structured output from triage agent  | 
60 |  | -      output = result.output || "[No output]"  | 
61 |  | -      if @context[:current_agent] == "Triage Agent" && output.is_a?(Hash)  | 
62 |  | -        # Display the response from structured response  | 
63 |  | -        puts "🤖 #{output["response"]}"  | 
64 |  | -        puts "\e[2m   [Intent]: #{output["intent"]}\e[0m" if output["intent"]  | 
65 |  | -      else  | 
66 |  | -        puts "🤖 #{output}"  | 
 | 62 | +        # Display callback messages if any  | 
 | 63 | +        if @callback_messages.any?  | 
 | 64 | +          puts dim_text(@callback_messages.join("\n"))  | 
 | 65 | +          @callback_messages.clear  | 
 | 66 | +        end  | 
 | 67 | + | 
 | 68 | +        # Handle structured output from agents  | 
 | 69 | +        output = result.output || "[No output]"  | 
 | 70 | + | 
 | 71 | +        if output.is_a?(Hash) && output.key?("response")  | 
 | 72 | +          # Display the response from structured response  | 
 | 73 | +          puts "🤖 #{output["response"]}"  | 
 | 74 | +          puts dim_text("   [Intent]: #{output["intent"]}") if output["intent"]  | 
 | 75 | +          puts dim_text("   [Sentiment]: #{output["sentiment"].join(", ")}") if output["sentiment"]&.any?  | 
 | 76 | +        else  | 
 | 77 | +          puts "🤖 #{output}"  | 
 | 78 | +        end  | 
 | 79 | + | 
 | 80 | +        puts # Add blank line after agent response  | 
 | 81 | +      rescue StandardError => e  | 
 | 82 | +        clear_status_line  | 
 | 83 | +        puts red("❌ Error: #{e.message}")  | 
 | 84 | +        puts dim_text("Please try again or type '/help' for assistance.")  | 
 | 85 | +        puts # Add blank line after error message  | 
67 | 86 |       end  | 
68 | 87 |     end  | 
69 | 88 |   end  | 
70 | 89 | 
 
  | 
71 | 90 |   private  | 
72 | 91 | 
 
  | 
73 | 92 |   def setup_callbacks  | 
 | 93 | +    @callback_messages = []  | 
 | 94 | + | 
74 | 95 |     @runner.on_agent_thinking do |agent_name, _input|  | 
75 |  | -      update_status("🧠 #{agent_name} is thinking...")  | 
 | 96 | +      message = "🧠 #{agent_name} is thinking..."  | 
 | 97 | +      update_status(message)  | 
 | 98 | +      @callback_messages << message  | 
76 | 99 |     end  | 
77 | 100 | 
 
  | 
78 | 101 |     @runner.on_tool_start do |tool_name, _args|  | 
79 |  | -      update_status("🔧 Using #{tool_name}...")  | 
 | 102 | +      message = "🔧 Using #{tool_name}..."  | 
 | 103 | +      update_status(message)  | 
 | 104 | +      @callback_messages << message  | 
80 | 105 |     end  | 
81 | 106 | 
 
  | 
82 | 107 |     @runner.on_tool_complete do |tool_name, _result|  | 
83 |  | -      update_status("✅ #{tool_name} completed")  | 
 | 108 | +      message = "✅ #{tool_name} completed"  | 
 | 109 | +      update_status(message)  | 
 | 110 | +      @callback_messages << message  | 
84 | 111 |     end  | 
85 | 112 | 
 
  | 
86 | 113 |     @runner.on_agent_handoff do |from_agent, to_agent, _reason|  | 
87 |  | -      update_status("🔄 Handoff: #{from_agent} → #{to_agent}")  | 
 | 114 | +      message = "🔄 Handoff: #{from_agent} → #{to_agent}"  | 
 | 115 | +      update_status(message)  | 
 | 116 | +      @callback_messages << message  | 
88 | 117 |     end  | 
89 | 118 |   end  | 
90 | 119 | 
 
  | 
91 | 120 |   def update_status(message)  | 
92 | 121 |     clear_status_line  | 
93 |  | -    print message  | 
 | 122 | +    print dim_text(message)  | 
94 | 123 |     $stdout.flush  | 
95 | 124 |   end  | 
96 | 125 | 
 
  | 
@@ -197,6 +226,27 @@ def get_agent_description(key)  | 
197 | 226 |     else "Unknown agent"  | 
198 | 227 |     end  | 
199 | 228 |   end  | 
 | 229 | + | 
 | 230 | +  # ANSI color helper methods  | 
 | 231 | +  def dim_text(text)  | 
 | 232 | +    "\e[90m#{text}\e[0m"  | 
 | 233 | +  end  | 
 | 234 | + | 
 | 235 | +  def green(text)  | 
 | 236 | +    "\e[32m#{text}\e[0m"  | 
 | 237 | +  end  | 
 | 238 | + | 
 | 239 | +  def yellow(text)  | 
 | 240 | +    "\e[33m#{text}\e[0m"  | 
 | 241 | +  end  | 
 | 242 | + | 
 | 243 | +  def red(text)  | 
 | 244 | +    "\e[31m#{text}\e[0m"  | 
 | 245 | +  end  | 
 | 246 | + | 
 | 247 | +  def cyan(text)  | 
 | 248 | +    "\e[36m#{text}\e[0m"  | 
 | 249 | +  end  | 
200 | 250 | end  | 
201 | 251 | 
 
  | 
202 | 252 | # Run the demo  | 
 | 
0 commit comments