ruby-openai-cli 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.MD +23 -0
- data/bin/ruby-openai-cli +3 -0
- data/lib/context.rb +7 -0
- data/lib/prompt.rb +140 -0
- data/lib/version.rb +1 -0
- metadata +135 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 3c5856a8ea31123a8d4e09f28f32bad0ac09a610cb82c980272c72deebc55f77
         | 
| 4 | 
            +
              data.tar.gz: c8c0b098bef65ced164868f30cb9956b11238419ab28e4365bc7300bb9dc2dc8
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 455d5a4b2354e8ba320ca8a964d42974f65d5bca5125170e3f2b04fda06210d2c47df11ef009f6f65389c2d2f95410b1466f666c05ed132cf26947469861d254
         | 
| 7 | 
            +
              data.tar.gz: 19d6bb2e7f9cabe0ad63378bf0ab9a6bb1f36d9adc7b40e7dda25681569cc59cf091e296f83ede2ce3a2e70a22e68153e710b364fc2f714f4bda8eff69696677
         | 
    
        data/README.MD
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            [](https://badge.fury.io/rb/ruby-openai-cli)
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ## Ruby-OpenAI-CLI
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            A tool to query OpenAI's GPT APIs from the command line.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ### Usage
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            To run the tool, you need an API key from OpenAI, you can get one here:
         | 
| 10 | 
            +
            https://platform.openai.com/account/api-keys
         | 
| 11 | 
            +
            You will be asked to provide on first use of `ruby-openai-cli`, or you can
         | 
| 12 | 
            +
            provide it as `OPENAI_API_KEY` in your environment.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            To install the gem, run: `gem install ruby-openai-cli`. Then you can 
         | 
| 15 | 
            +
            use the tool by calling `ruby-openai-cli` on your command line.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ### Development
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            * Install dev dependencies: `gem install awesome_print byebug rubocop`
         | 
| 22 | 
            +
            * Build Rubygem: `gem build ruby-openai-cli.gemspec`
         | 
| 23 | 
            +
            * Publish Rubygem: `gem push ruby-openai-cli-1.0.gem`
         | 
    
        data/bin/ruby-openai-cli
    ADDED
    
    
    
        data/lib/context.rb
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            DEFAULT_CONTEXTS = {
         | 
| 2 | 
            +
              "ruby": 'Imagine you are a senior software developer specialized in Ruby, ' \
         | 
| 3 | 
            +
                      'Ruby on Rails and SQL with 20 years of experience. ' \
         | 
| 4 | 
            +
                      'You are very proficient at writing high quality, clean, concise and highly ' \
         | 
| 5 | 
            +
                      'optimized code that follows the standards. You are my personal assistant. ' \
         | 
| 6 | 
            +
                      'You are here to help me in various tasks and answer my questions.'
         | 
| 7 | 
            +
            }
         | 
    
        data/lib/prompt.rb
    ADDED
    
    | @@ -0,0 +1,140 @@ | |
| 1 | 
            +
            require 'openai'
         | 
| 2 | 
            +
            require 'dotenv/load'
         | 
| 3 | 
            +
            require 'optparse'
         | 
| 4 | 
            +
            require 'tty-markdown' # https://github.com/piotrmurach/tty-markdown
         | 
| 5 | 
            +
            require_relative 'context'
         | 
| 6 | 
            +
            require_relative 'version'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            CONFIG_DIR = Dir.home + '/.config/ruby-openai-cli'
         | 
| 9 | 
            +
            CHATS_DIR = CONFIG_DIR + '/chats'
         | 
| 10 | 
            +
            API_KEY_FILE = CONFIG_DIR + '/api_key'
         | 
| 11 | 
            +
            CONTEXTS_FILE = CONFIG_DIR + '/contexts.json'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            MODEL = 'gpt-3.5-turbo'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            options = {}
         | 
| 16 | 
            +
            bin_name = File.basename($0)
         | 
| 17 | 
            +
            OptionParser.new do |opts|
         | 
| 18 | 
            +
              opts.banner = "#{bin_name}, version: #{VERSION}. Usage: #{bin_name} [options]"
         | 
| 19 | 
            +
              opts.on('-q', '--question QUESTION', 'Provide question as parameter and receive answer to stdout') do |v|
         | 
| 20 | 
            +
                options[:q] = v
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
              opts.on('--context CONTEXT', 'Use a context') { |v| options[:context] = v }
         | 
| 23 | 
            +
              opts.on('--list-contexts', 'List available contexts') { |_v| options[:lc] = true }
         | 
| 24 | 
            +
              opts.on('-d', '--debug', 'Debug mode') { |_v| options[:d] = true }
         | 
| 25 | 
            +
              opts.on('-c', '--chat [NAME]', 'Start an interactive conversation (saved as NAME). ' \
         | 
| 26 | 
            +
                                             'You can continue a previous chat by providing the same name') do |name|
         | 
| 27 | 
            +
                options[:chat] = true
         | 
| 28 | 
            +
                FileUtils.mkdir_p(CHATS_DIR) if name
         | 
| 29 | 
            +
                options[:chat_name] = name
         | 
| 30 | 
            +
                options[:chat_file] = CHATS_DIR + "/#{name}.json"
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
              opts.on('-l', '--list-chats', 'List stored chats') { |_v| options[:l] = true }
         | 
| 33 | 
            +
            end.parse!
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            unless ENV.fetch('OPENAI_API_KEY', nil)
         | 
| 36 | 
            +
              unless File.exist?(API_KEY_FILE)
         | 
| 37 | 
            +
                puts 'To use the OpenAI API, you need to get API key at https://platform.openai.com/account/api-keys.'
         | 
| 38 | 
            +
                puts "It will get stored at #{API_KEY_FILE}."
         | 
| 39 | 
            +
                puts 'Please enter your API key:'
         | 
| 40 | 
            +
                FileUtils.mkdir_p(CONFIG_DIR)
         | 
| 41 | 
            +
                key = gets.chomp
         | 
| 42 | 
            +
                test_response = OpenAI::Client.new(access_token: key).models.list
         | 
| 43 | 
            +
                unless test_response.code == 200
         | 
| 44 | 
            +
                  puts test_response['error']['message']
         | 
| 45 | 
            +
                  exit 1
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
                File.write(API_KEY_FILE, key)
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
              ENV['OPENAI_API_KEY'] = File.read(API_KEY_FILE)
         | 
| 50 | 
            +
            end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            OpenAI.configure do |config|
         | 
| 53 | 
            +
              config.access_token = ENV.fetch('OPENAI_API_KEY')
         | 
| 54 | 
            +
              config.request_timeout = 240
         | 
| 55 | 
            +
            end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            prompt = ''
         | 
| 58 | 
            +
            client = OpenAI::Client.new
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            def get_input
         | 
| 61 | 
            +
              printf TTY::Markdown.parse('**Your message>**', theme: { strong: %i[yellow bold] })
         | 
| 62 | 
            +
              gets.chomp
         | 
| 63 | 
            +
            end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            def format_input(input)
         | 
| 66 | 
            +
              puts TTY::Markdown.parse("**Your message>**\n" + input + "\n",
         | 
| 67 | 
            +
                                       theme: { strong: %i[yellow bold] })
         | 
| 68 | 
            +
            end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            def format_response(response)
         | 
| 71 | 
            +
              puts TTY::Markdown.parse("\n**ChatGPT response:**\n" + response + "\n\n",
         | 
| 72 | 
            +
                                       theme: { strong: %i[blue bold] })
         | 
| 73 | 
            +
            end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            if options[:lc]
         | 
| 76 | 
            +
              puts 'Available contexts: '
         | 
| 77 | 
            +
              DEFAULT_CONTEXTS.keys.each do |ctx|
         | 
| 78 | 
            +
                puts ctx
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
              exit
         | 
| 81 | 
            +
            end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            if options[:l]
         | 
| 84 | 
            +
              out = "**Available chats:**\n\n"
         | 
| 85 | 
            +
              Dir[CHATS_DIR + '/*'].each do |file|
         | 
| 86 | 
            +
                out += "* '#{File.basename(file, '.json')}'\n"
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
              out += "\nYou can continue a chat with: `#{bin_name} -c '<chat name>'`"
         | 
| 89 | 
            +
              out += "\nYour chats are stored at `#{CHATS_DIR}`"
         | 
| 90 | 
            +
              puts TTY::Markdown.parse(out)
         | 
| 91 | 
            +
              exit
         | 
| 92 | 
            +
            end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            messages_history = []
         | 
| 95 | 
            +
            if options[:chat_name] && File.exist?(options[:chat_file])
         | 
| 96 | 
            +
              messages_history = JSON.parse(File.read(options[:chat_file]))
         | 
| 97 | 
            +
              messages_history.each do |message|
         | 
| 98 | 
            +
                format_input(message['content']) if message['role'] == 'user'
         | 
| 99 | 
            +
                format_response(message['content']) if message['role'] == 'assistant'
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
            end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            if options[:context]
         | 
| 104 | 
            +
              context = DEFAULT_CONTEXTS[options[:context].to_sym] || options[:context]
         | 
| 105 | 
            +
              messages_history << { role: 'system', content: context }
         | 
| 106 | 
            +
            end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            begin
         | 
| 109 | 
            +
              loop do
         | 
| 110 | 
            +
                prompt = options[:q]
         | 
| 111 | 
            +
                prompt = get_input unless options[:q]
         | 
| 112 | 
            +
                options[:q] = nil
         | 
| 113 | 
            +
                messages_history << { "role": 'user', "content": prompt }
         | 
| 114 | 
            +
                parameters = {
         | 
| 115 | 
            +
                  model: MODEL,
         | 
| 116 | 
            +
                  messages: messages_history,
         | 
| 117 | 
            +
                  temperature: 0.3, # low temperature = very high probability response (0 to 1)
         | 
| 118 | 
            +
                  max_tokens: 2000
         | 
| 119 | 
            +
                }
         | 
| 120 | 
            +
                puts "Sending: #{parameters}" if options[:d]
         | 
| 121 | 
            +
                response = client.chat(parameters:)
         | 
| 122 | 
            +
                puts "Received: #{response}" if options[:d]
         | 
| 123 | 
            +
                begin
         | 
| 124 | 
            +
                  response_text = response['choices'][0]['message']['content']
         | 
| 125 | 
            +
                  if options[:chat]
         | 
| 126 | 
            +
                    format_response response_text
         | 
| 127 | 
            +
                  else
         | 
| 128 | 
            +
                    puts response_text
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
                  messages_history << { "role": 'assistant', "content": response_text }
         | 
| 131 | 
            +
                  File.write(options[:chat_file], messages_history.to_json) if options[:chat_name]
         | 
| 132 | 
            +
                rescue StandardError
         | 
| 133 | 
            +
                  puts "Error: '#{response['error']['message']}'"
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
                break unless options[:chat]
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
            rescue Interrupt
         | 
| 138 | 
            +
              puts "Your current chat was saved as '#{options[:chat_name]}' at #{options[:chat_file]}" if options[:chat_name]
         | 
| 139 | 
            +
              exit 1
         | 
| 140 | 
            +
            end
         | 
    
        data/lib/version.rb
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            VERSION = '1.0.1'
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,135 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: ruby-openai-cli
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 1.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Thomas Schmidt
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2023-04-01 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: ruby-openai
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '3.7'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '3.7'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: optparse
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0.3'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0.3'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: tty-markdown
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0.7'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0.7'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: awesome_print
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - "~>"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '1.9'
         | 
| 62 | 
            +
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '1.9'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: byebug
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - "~>"
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '11.1'
         | 
| 76 | 
            +
              type: :development
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - "~>"
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '11.1'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: rubocop
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - "~>"
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '1.48'
         | 
| 90 | 
            +
              type: :development
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - "~>"
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '1.48'
         | 
| 97 | 
            +
            description: Using ChatGPT from the command line.
         | 
| 98 | 
            +
            email: tom@digitalflow.de
         | 
| 99 | 
            +
            executables:
         | 
| 100 | 
            +
            - ruby-openai-cli
         | 
| 101 | 
            +
            extensions: []
         | 
| 102 | 
            +
            extra_rdoc_files: []
         | 
| 103 | 
            +
            files:
         | 
| 104 | 
            +
            - README.MD
         | 
| 105 | 
            +
            - bin/ruby-openai-cli
         | 
| 106 | 
            +
            - lib/context.rb
         | 
| 107 | 
            +
            - lib/prompt.rb
         | 
| 108 | 
            +
            - lib/version.rb
         | 
| 109 | 
            +
            homepage: https://github.com/digitaltom/ruby-openai-cli
         | 
| 110 | 
            +
            licenses:
         | 
| 111 | 
            +
            - MIT
         | 
| 112 | 
            +
            metadata:
         | 
| 113 | 
            +
              homepage_uri: https://github.com/digitaltom/ruby-openai-cli
         | 
| 114 | 
            +
              source_code_uri: https://github.com/digitaltom/ruby-openai-cli
         | 
| 115 | 
            +
              changelog_uri: https://github.com/digitaltom/ruby-openai-cli
         | 
| 116 | 
            +
            post_install_message: 
         | 
| 117 | 
            +
            rdoc_options: []
         | 
| 118 | 
            +
            require_paths:
         | 
| 119 | 
            +
            - lib
         | 
| 120 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
              requirements:
         | 
| 122 | 
            +
              - - ">="
         | 
| 123 | 
            +
                - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                  version: '0'
         | 
| 125 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 126 | 
            +
              requirements:
         | 
| 127 | 
            +
              - - ">="
         | 
| 128 | 
            +
                - !ruby/object:Gem::Version
         | 
| 129 | 
            +
                  version: '0'
         | 
| 130 | 
            +
            requirements: []
         | 
| 131 | 
            +
            rubygems_version: 3.4.6
         | 
| 132 | 
            +
            signing_key: 
         | 
| 133 | 
            +
            specification_version: 4
         | 
| 134 | 
            +
            summary: CLI interface for OpenAI ChatGPT
         | 
| 135 | 
            +
            test_files: []
         |