|
| 1 | +#!/usr/bin/env ruby |
| 2 | +# frozen_string_literal: true |
| 3 | + |
| 4 | +# Docker Chat - Interactive command line tool |
| 5 | +# A comprehensive chat interface with all Docker tools available |
| 6 | +# |
| 7 | +# Usage: |
| 8 | +# ruby examples/docker_chat.rb |
| 9 | +# |
| 10 | +# Commands: |
| 11 | +# /exit - Exit the chat |
| 12 | +# /help - Show available Docker tools |
| 13 | +# /tools - List all loaded tools |
| 14 | +# /clear - Clear the screen |
| 15 | +# anything else - Send to OpenAI with Docker tools available |
| 16 | + |
| 17 | +require_relative '../lib/ruby_llm/docker' |
| 18 | +require 'io/console' |
| 19 | + |
| 20 | +# rubocop:disable Metrics/ClassLength |
| 21 | +class DockerChat |
| 22 | + def initialize |
| 23 | + check_environment |
| 24 | + configure_ruby_llm |
| 25 | + setup_chat |
| 26 | + @running = true |
| 27 | + end |
| 28 | + |
| 29 | + def start |
| 30 | + show_welcome |
| 31 | + main_loop |
| 32 | + show_goodbye |
| 33 | + end |
| 34 | + |
| 35 | + private |
| 36 | + |
| 37 | + def check_environment |
| 38 | + return if ENV['OPENAI_API_KEY'] |
| 39 | + |
| 40 | + puts '❌ Error: Please set OPENAI_API_KEY environment variable' |
| 41 | + puts "Example: export OPENAI_API_KEY='your-api-key-here'" |
| 42 | + exit 1 |
| 43 | + end |
| 44 | + |
| 45 | + def configure_ruby_llm |
| 46 | + RubyLLM.configure do |config| |
| 47 | + config.openai_api_key = ENV.fetch('OPENAI_API_KEY', nil) |
| 48 | + end |
| 49 | + end |
| 50 | + |
| 51 | + def setup_chat |
| 52 | + @chat = RubyLLM.chat(model: 'gpt-4') |
| 53 | + |
| 54 | + # Add all Docker tools to the chat |
| 55 | + RubyLLM::Docker.add_all_tools_to_chat(@chat) |
| 56 | + |
| 57 | + puts '🔧 Loading Docker tools...' |
| 58 | + puts "✅ Loaded #{RubyLLM::Docker.all_tools.size} Docker tools" |
| 59 | + end |
| 60 | + |
| 61 | + def show_welcome |
| 62 | + puts "\n#{'=' * 60}" |
| 63 | + puts '🐳 Welcome to Docker Chat!' |
| 64 | + puts ' Interactive CLI with all Docker tools available' |
| 65 | + puts '=' * 60 |
| 66 | + puts |
| 67 | + puts '💡 You can ask questions about Docker containers, images, networks, and volumes.' |
| 68 | + puts ' OpenAI has access to all Docker management tools on this system.' |
| 69 | + puts |
| 70 | + puts 'Commands:' |
| 71 | + puts ' /exit - Exit the chat' |
| 72 | + puts ' /help - Show available Docker tools' |
| 73 | + puts ' /tools - List all loaded tools' |
| 74 | + puts ' /clear - Clear the screen' |
| 75 | + puts |
| 76 | + puts '🚀 Ready! Type your questions or commands...' |
| 77 | + puts |
| 78 | + end |
| 79 | + |
| 80 | + def main_loop |
| 81 | + while @running |
| 82 | + print "\n🐳 > " |
| 83 | + |
| 84 | + begin |
| 85 | + input = gets&.chomp |
| 86 | + |
| 87 | + # Handle Ctrl+C or EOF |
| 88 | + if input.nil? |
| 89 | + @running = false |
| 90 | + break |
| 91 | + end |
| 92 | + |
| 93 | + process_input(input.strip) |
| 94 | + rescue Interrupt |
| 95 | + puts "\n\n👋 Received interrupt signal. Use /exit to quit cleanly." |
| 96 | + rescue StandardError => e |
| 97 | + puts "❌ Error: #{e.message}" |
| 98 | + puts ' Please try again or type /exit to quit.' |
| 99 | + end |
| 100 | + end |
| 101 | + end |
| 102 | + |
| 103 | + def process_input(input) |
| 104 | + return if input.empty? |
| 105 | + |
| 106 | + case input.downcase |
| 107 | + when '/exit', '/quit', '/q' |
| 108 | + @running = false |
| 109 | + when '/help', '/h' |
| 110 | + show_help |
| 111 | + when '/tools', '/t' |
| 112 | + show_tools |
| 113 | + when '/clear', '/c' |
| 114 | + clear_screen |
| 115 | + when input.start_with?('/') |
| 116 | + puts "❓ Unknown command: #{input}" |
| 117 | + puts ' Type /help for available commands' |
| 118 | + else |
| 119 | + handle_chat_message(input) |
| 120 | + end |
| 121 | + end |
| 122 | + |
| 123 | + def handle_chat_message(message) |
| 124 | + puts "\n🤔 Thinking..." |
| 125 | + |
| 126 | + begin |
| 127 | + response = @chat.ask(message) |
| 128 | + |
| 129 | + puts "\n🤖 OpenAI Response:" |
| 130 | + puts '─' * 50 |
| 131 | + puts response.content |
| 132 | + puts '─' * 50 |
| 133 | + rescue StandardError => e |
| 134 | + puts "\n❌ Error communicating with OpenAI:" |
| 135 | + puts " #{e.class}: #{e.message}" |
| 136 | + puts ' Please check your API key and network connection.' |
| 137 | + end |
| 138 | + end |
| 139 | + |
| 140 | + def show_help |
| 141 | + puts "\n📚 Available Docker Tools:" |
| 142 | + puts '─' * 50 |
| 143 | + |
| 144 | + tools_by_category = { |
| 145 | + 'Container Management' => [ |
| 146 | + 'ListContainers - List all Docker containers', |
| 147 | + 'CreateContainer - Create new containers', |
| 148 | + 'RunContainer - Create and start containers', |
| 149 | + 'StartContainer - Start stopped containers', |
| 150 | + 'StopContainer - Stop running containers', |
| 151 | + 'RemoveContainer - Delete containers', |
| 152 | + 'RecreateContainer - Recreate containers with same config', |
| 153 | + 'ExecContainer - Execute commands inside containers', |
| 154 | + 'CopyToContainer - Copy files to containers', |
| 155 | + 'FetchContainerLogs - Get container logs' |
| 156 | + ], |
| 157 | + 'Image Management' => [ |
| 158 | + 'ListImages - List available Docker images', |
| 159 | + 'PullImage - Download images from registries', |
| 160 | + 'BuildImage - Build images from Dockerfile', |
| 161 | + 'TagImage - Tag images with new names', |
| 162 | + 'PushImage - Upload images to registries', |
| 163 | + 'RemoveImage - Delete images' |
| 164 | + ], |
| 165 | + 'Network Management' => [ |
| 166 | + 'ListNetworks - List Docker networks', |
| 167 | + 'CreateNetwork - Create custom networks', |
| 168 | + 'RemoveNetwork - Delete networks' |
| 169 | + ], |
| 170 | + 'Volume Management' => [ |
| 171 | + 'ListVolumes - List Docker volumes', |
| 172 | + 'CreateVolume - Create persistent volumes', |
| 173 | + 'RemoveVolume - Delete volumes' |
| 174 | + ] |
| 175 | + } |
| 176 | + |
| 177 | + tools_by_category.each do |category, tools| |
| 178 | + puts "\n#{category}:" |
| 179 | + tools.each { |tool| puts " • #{tool}" } |
| 180 | + end |
| 181 | + |
| 182 | + puts "\n💡 Example questions you can ask:" |
| 183 | + puts " • 'How many containers are running?'" |
| 184 | + puts " • 'Show me all Docker images'" |
| 185 | + puts " • 'Create a new nginx container named web-server'" |
| 186 | + puts " • 'What networks are available?'" |
| 187 | + puts " • 'Pull the latest Ubuntu image'" |
| 188 | + end |
| 189 | + |
| 190 | + def show_tools |
| 191 | + puts "\n🔧 Loaded Tools (#{RubyLLM::Docker.all_tools.size}):" |
| 192 | + puts '─' * 30 |
| 193 | + |
| 194 | + RubyLLM::Docker.all_tools.each_with_index do |tool_class, index| |
| 195 | + tool_name = tool_class.name.split('::').last |
| 196 | + puts "#{(index + 1).to_s.rjust(2)}. #{tool_name}" |
| 197 | + end |
| 198 | + end |
| 199 | + |
| 200 | + def clear_screen |
| 201 | + system('clear') || system('cls') |
| 202 | + puts '🐳 Docker Chat - Screen cleared' |
| 203 | + end |
| 204 | + |
| 205 | + def show_goodbye |
| 206 | + puts "\n👋 Thanks for using Docker Chat!" |
| 207 | + puts ' Hope you found it helpful for managing your Docker environment.' |
| 208 | + puts |
| 209 | + end |
| 210 | +end |
| 211 | +# rubocop:enable Metrics/ClassLength |
| 212 | + |
| 213 | +# Start the chat if this file is run directly |
| 214 | +if __FILE__ == $PROGRAM_NAME |
| 215 | + begin |
| 216 | + DockerChat.new.start |
| 217 | + rescue StandardError => e |
| 218 | + puts "\n💥 Fatal error: #{e.class} - #{e.message}" |
| 219 | + puts ' Please check your setup and try again.' |
| 220 | + exit 1 |
| 221 | + end |
| 222 | +end |
0 commit comments