enum OpenAIMode Chat Complete Insert def self.from_s(str_mode) return OpenAIMode.values.find do |openai_mode| openai_mode.to_s.downcase == str_mode.downcase end end end class GptConfig # defaults property gpt_temperature = 0.82 property gpt_presence_penalty = 1 property gpt_frequency_penalty = 1 property gpt_max_tokens = 256 property gpt_mode = OpenAIMode::Chat end class AppConfig property input_file = STDIN property input_file_path = "" property output_file = STDOUT property output_file_path = "" property past_characters_limit = 1000 property use_color = true property make_request = true property verbose = false property gpt_config = GptConfig.new end class Storyteller property config = AppConfig.new def initialize() end def prepare() self._parse_config() self._parse_command_line(ARGV) end private def _parse_config() end private def _parse_command_line(args) parser = OptionParser.new do |parser| parser.banner = "Usage: storyteller [options]" parser.separator("Options:") parser.on("-i FILE", "--input=FILE", "Path to input file") do |file| @config.input_file_path = file end parser.on("-h", "--help", "Show this help") do puts parser exit end parser.on("-n", "--no-color", "Disable color output") do @config.use_color = false end parser.on("-o FILE", "--output=FILE", "Path to output file") do |file| @config.use_color = false @config.output_file_path = file end parser.on("-v", "--verbose", "Be verbose (cumulative)") do @config.verbose = true end parser.on("--dry-run", "Don't call the API") do @config.make_request = false end parser.separator("GPT options") parser.on("--gpt-mode MODE", "GPT mode (chat,insert,complete) (default: chat)") do |chosen_mode| result_mode = OpenAIMode.from_s(chosen_mode.downcase) if result_mode.nil? STDERR.puts "ERROR: unknown mode #{chosen_mode}" exit 1 end gpt_mode = result_mode unless result_mode.nil? end parser.on("--gpt-temperature TEMPERATURE", "GPT Temperature") do |temperature| gpt_temperature = temperature end parser.on("--gpt-presence-penalty PENALTY", "GPT Presence Penalty") do |presence_penalty| gpt_presence_penalty = presence_penalty end parser.on("--gpt-frequency-penalty PENALTY", "GPT Frequency Penalty") do |frequency_penalty| gpt_frequency_penalty = frequency_penalty end parser.on("--gpt-max-tokens TOKENS", "GPT Max Tokens") do |max_tokens| gpt_max_tokens = max_tokens end end parser.parse(args) end def load_file() # Read file and initialize zones if !@config.input_file_path.empty? # puts "d: Using input file #{input_file_path}" @config.input_file = File.open(@config.input_file_path) end prompt = self.read_file(@config.input_file) @config.input_file.close prompt end def run() original_prompt = self.load_file() # Build GPT-3 request prompt = self.complete(original_prompt, @config.make_request, @config.verbose) pp prompt if @config.verbose exit 0 if !@config.make_request if !@config.output_file_path.empty? # puts "d: Using output file #{input_file_path}" @config.output_file = File.open(@config.output_file_path, "w") end self.write_file(@config.output_file, prompt, @config.use_color) @config.output_file.close # rescue ex : OptionParser::InvalidOption # STDERR.puts @parser # STDERR.puts "\nERROR: #{ex.message}" # exit 1 # rescue ex: OpenAI::Client::ClientError # STDERR.puts "ERROR: #{ex.message}" # exit 1 end def complete(prompt : Prompt, make_request : Bool, verbose : Bool) builder = Builder::OpenAIChat.new(verbose: verbose) messages = builder.build(prompt) STDERR.puts messages if verbose return prompt if !make_request channel_ready = Channel(Bool).new channel_tick = Channel(Bool).new # sp = Spin.new(0.5, Spinner::Charset[:progress]) spawn do tick = 0 loop do tick += 1 print "." if tick > (3 * 60) print "(timeout)" exit 1 end # channel_tick.send(true) sleep 1.seconds end end spawn do openai = OpenAI::Client.new(access_token: ENV.fetch("OPENAI_API_KEY")) result = openai.chat( "gpt-3.5-turbo", messages, { "temperature" => 0.82, "presence_penalty" => 1, "frequency_penalty" => 1, "max_tokens" => 256 } ) prompt.present_zone.content << result.choices.first["message"]["content"] channel_ready.send(true) end # sp.start channel_ready.receive channel_ready.close # sp.stop prompt end def read_file(input_file : IO::FileDescriptor) content = input_file.gets_to_end # puts "d: building parser" parser = Parser::PromptString.new # puts "d: parsing" prompt = parser.parse(content) # pp prompt end def write_file(output_file : IO::FileDescriptor, prompt : Prompt, use_color : Bool) # STDERR.puts "d: building builder" builder = Builder::PromptString.new(use_color) # STDERR.puts "d: building" text = builder.build(prompt) output_file.write_string(text.to_slice) end def display_completion(completion : String) # Code pour afficher la complétion end end