clai 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4e8a4ba69b4bb0403208ae9d6a6d45b90668447ac35606761c546dae4e51451a
4
+ data.tar.gz: e3bc9eb1ffdb659059ce249cf88334abef0456f504945b3ddb243116bb756374
5
+ SHA512:
6
+ metadata.gz: c3a31a975f7a3b0a623ae19d895a5ede3ff3156b562f10367d2e6597ecdad1b3713b29874e35b292ac4c6ad8df54494b526486083280da2753bdbd6f9d66b39d
7
+ data.tar.gz: 4a14d13fda0e91c52c27f3c1cd1cd9b1e6501ce142d397310b650c8277c9f9d6cacb152f6f007e45a23a456b66c32ec1871ee721524c5805ed3e44b400d94930
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,19 @@
1
+ # Contributing
2
+
3
+ The following topics are missing for a 1.0.0 release:
4
+
5
+ ```
6
+ * Allow adding "personas" to the config ([see here](docs/personas.md))
7
+ * Rewrite with tests as soon as the basic prototype is useful
8
+ * Add GitHub Actions for PRs
9
+ * Build a landing page
10
+ ```
11
+
12
+ Please use `clai` and contribute here on GitHub. I'm open to all suggestions and improvements.
13
+
14
+ 0.x.x versions don't need tests or extensive documentation. Adding a small description will help me understand your change though :)
15
+
16
+ Please open a PR with a short description of your use-case and I will review and merge.
17
+
18
+ Thank you,
19
+ Niklas
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Niklas Häusele
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # CLAI - CLI + AI in your terminal
2
+
3
+ [![MIT](https://img.shields.io/badge/license-mit-green?style=for-the-badge)](LICENSE)
4
+ ![Ruby](https://img.shields.io/badge/ruby-%23CC342D.svg?style=for-the-badge&logo=ruby&logoColor=white)
5
+ ![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)
6
+
7
+ ![Demo](demo.gif)
8
+
9
+ CLAI (CLI + AI = CLAI) allows you to interact with the OpenAI API in your terminal.
10
+
11
+ ## Installation
12
+
13
+ First, ensure that you have Ruby installed on your system. Then, you can install the gem by running:
14
+
15
+ ```
16
+ $ gem install clai
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ Before using the CLI tool, you need to configure it with your OpenAI API key. You can obtain an API key from the OpenAI website. Once you have your API key, run the following command and provide your key when prompted:
22
+
23
+ ```
24
+ $ clai setup
25
+ ```
26
+
27
+ This will create a configuration file (`~/.config/clai/clai.yml`) on your system.
28
+
29
+ Read more about the setup command [here](docs/setup.md).
30
+
31
+ ## Usage
32
+
33
+ You can chat with ChatGPT with:
34
+
35
+ ```
36
+ $ clai chat "Tell me a funny joke"
37
+ ```
38
+
39
+ This will send the given prompt to the API and display the generated completion.
40
+
41
+ ### Help
42
+
43
+ To view the available commands and options, you can use the `help` command:
44
+
45
+ ```
46
+ $ clai help
47
+ ```
48
+
49
+ This will display a list of supported commands and their descriptions.
50
+
51
+ ### Starting a session
52
+
53
+ You can start an interactive session with `clai session` command.
54
+
55
+ ```
56
+ $ clai session
57
+ Your prompt: <Enter your prompt here><Enter>
58
+ ```
59
+
60
+ This will start a REPL like process to interact with the OpenAI api.
61
+
62
+ ## Examples
63
+
64
+ Here are a few examples to get you started:
65
+
66
+ ```sh
67
+ # Setup clai on your machine
68
+ $ clai setup
69
+
70
+ # Start an interactive chat session
71
+ $ clai session
72
+
73
+ # Get help about the available commands and options
74
+ $ clai help
75
+
76
+ # Send a single prompt
77
+ $ clai chat "Hello World"
78
+ ```
79
+
80
+ ## Contributing
81
+
82
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for more information.
83
+
84
+ ## Disclaimer
85
+
86
+ This is alpha software. Make sure to set a billing limit for your api key or unexpected costs will occur.
87
+
88
+ ## License
89
+
90
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ task default: :test
data/docs/chat.md ADDED
@@ -0,0 +1,10 @@
1
+ # Chat
2
+
3
+ Send a single prompt to the completions api.
4
+
5
+ Some examples:
6
+
7
+
8
+ ```
9
+ $ clai chat "Tell me a funny joke"
10
+ ```
data/docs/intro.md ADDED
@@ -0,0 +1,10 @@
1
+ # Introduction
2
+
3
+ You can think of `clai` as ChatGPT for the terminal.
4
+ It's very basic at the moment. You can think of it as ChatGPT for the terminal. I'm currently trying to incorporate clai in my daily work, so I'm still experimenting with the API.
5
+
6
+ `clai` currently supports OpenAI and needs a paid API key to work.
7
+
8
+ My main use case currently is to have a terminal opened with a clai session and ask questions instead of googling.
9
+
10
+ 0.x.0 will be unstable and untested, I'm currently aiming to release a tested 1.0.0 soon. Please see [here](https://github.com/codergeek121/clai/CONTRIBUTING.MD) on how to contribute at this stage of the project.
data/docs/personas.md ADDED
@@ -0,0 +1,23 @@
1
+ # Personas
2
+
3
+ TODO: This feature is still under development!
4
+
5
+ A clai setup should allow preconfiguring multiple personas. Each command should support a `--persona` option to allow setting some default prompts to the api.
6
+
7
+ An example could look like this:
8
+
9
+ ```yaml
10
+ personas:
11
+ work:
12
+ - Use professional language
13
+ - Keep your answer below 100 words
14
+ bavarian:
15
+ - Use a bavarian accent
16
+
17
+ # Starting a session with a preselected persona
18
+ $ clai session --persona bavarian
19
+ Your prompt: <Your prompt><Enter>
20
+ Some bavarian answer.
21
+ ```
22
+
23
+ The default setup should have useful personas as a default.
data/docs/session.md ADDED
@@ -0,0 +1,3 @@
1
+ # Session
2
+
3
+ TODO
data/docs/setup.md ADDED
@@ -0,0 +1,3 @@
1
+ # Setup
2
+
3
+ TODO
data/exe/clai ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "clai"
6
+
7
+ CLAI::CLI.start(ARGV)
data/lib/clai/cli.rb ADDED
@@ -0,0 +1,42 @@
1
+ module CLAI
2
+ class CLI < Thor
3
+ default_task :session
4
+
5
+ desc "session", "Start an interactive chat session"
6
+ long_desc <<~DESC
7
+ Starts a REPL like chat process
8
+
9
+ > $ clai session
10
+ DESC
11
+ def session
12
+ Commands::Session.new(config).start_session
13
+ end
14
+
15
+ desc "chat", "Prompt on the terminal"
16
+ long_desc <<~DESC
17
+ Send a single prompt to AI
18
+
19
+ > $ clai chat "Tell me a short story!"
20
+ DESC
21
+ def chat(prompt)
22
+ Commands::Chat.new(config).chat(prompt)
23
+ end
24
+
25
+ option :force, type: :boolean, default: false
26
+ desc "setup", "Setup clai on your machine"
27
+ long_desc <<~DESC
28
+ Setup `clai` on your machine. This will ask you for a api key and create a base config file.
29
+
30
+ > $ clai setup
31
+ DESC
32
+ def setup
33
+ Commands::Setup.new.run(options[:force])
34
+ end
35
+
36
+ private
37
+
38
+ def config
39
+ @config ||= Config.parse
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,13 @@
1
+ module CLAI
2
+ module Commands
3
+ class Chat
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def chat(prompt)
9
+ HTTPClient.new(@config).chat(prompt)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module CLAI
2
+ module Commands
3
+ class Session
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def start_session
9
+ loop do
10
+ print S.(:prompt)
11
+ question = $stdin.gets
12
+ break unless question
13
+ HTTPClient.new(@config).chat(question.strip)
14
+ puts
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ module CLAI
2
+ module Commands
3
+ class Setup
4
+ def run(force)
5
+ if clai_setup? && !force
6
+ puts S.(:already_setup)
7
+ else
8
+ generate_config_directory
9
+ ask_for_open_ai_api_key
10
+ generate_sample_config_file
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def clai_setup?
17
+ File.exist?(File.expand_path("~/.config/clai/clai.yml"))
18
+ end
19
+
20
+ def ask_for_open_ai_api_key
21
+ puts S.(:disclaimer)
22
+ print S.(:paste_key)
23
+ input = $stdin.gets.strip
24
+ if input.length > 0
25
+ @api_key = input
26
+ else
27
+ @api_key = "<No API key specified>"
28
+ end
29
+ end
30
+
31
+ def generate_config_directory
32
+ FileUtils.mkdir_p(File.expand_path("~/.config/clai"))
33
+ end
34
+
35
+ def generate_sample_config_file
36
+ File.write(CLAI.config_file_path, sample_config)
37
+ end
38
+
39
+ # TODO: use erubis and move to /strings and rename /strings
40
+ def sample_config
41
+ <<~YML
42
+ # Read more about the clai.yml config file at https://github.com/codergeek121/clai/docs/setup.md
43
+ api_key: #{@api_key}
44
+ model: gpt-3.5-turbo
45
+ YML
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ module CLAI
2
+ class Config
3
+ def self.parse
4
+ config = YAML.safe_load_file(CLAI.config_file_path)
5
+ new(api_key: config["api_key"], model: config["model"])
6
+ rescue Errno::ENOENT
7
+ puts S.(:no_clai_yml)
8
+ exit(false)
9
+ end
10
+
11
+ attr_reader :api_key, :model
12
+
13
+ def initialize(api_key:, model:)
14
+ @api_key = api_key
15
+ @model = model
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,48 @@
1
+ module CLAI
2
+ class HTTPClient
3
+ def initialize(config)
4
+ @config = config
5
+ end
6
+
7
+ def chat(query)
8
+ puts
9
+ client
10
+ .post("https://api.openai.com/v1/chat/completions",
11
+ json: {
12
+ model: @config.model,
13
+ messages: [
14
+ { "role": "user", "content": query }
15
+ ],
16
+ stream: true
17
+ }
18
+ ).body.each_with_index do |chunk, i|
19
+ if i == 0
20
+ # TODO: remove workaround
21
+ splits = chunk.split("\n\n")
22
+ if splits.length > 1
23
+ chunk = splits[1]
24
+ else
25
+ chunk = splits[0]
26
+ end
27
+ end
28
+ data = chunk.match(/data: (.*)/) || []
29
+
30
+ break if data[1] == "[DONE]"
31
+
32
+ if data[1]
33
+ result = JSON.parse(data[1])
34
+ print result.dig('choices', 0, "delta", "content")
35
+ end
36
+ end
37
+ puts
38
+ end
39
+
40
+ private
41
+
42
+ def client
43
+ HTTP
44
+ .auth("Bearer #{@config.api_key}")
45
+ .headers(accept: "application/json")
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CLAI
4
+ VERSION = "0.1.1"
5
+ end
data/lib/clai.rb ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require "http"
5
+ require "zeitwerk"
6
+ require "debug"
7
+ require "fileutils"
8
+ require "yaml"
9
+
10
+ loader = Zeitwerk::Loader.for_gem
11
+ loader.inflector.inflect(
12
+ "clai" => "CLAI",
13
+ "http_client" => "HTTPClient",
14
+ "cli" => "CLI",
15
+ )
16
+ loader.setup
17
+
18
+ module CLAI
19
+ class Error < StandardError; end
20
+ class APIKeyMissing < Error; end
21
+
22
+ @@config_file_path = File.expand_path("~/.config/clai/clai.yml")
23
+ def self.config_file_path
24
+ @@config_file_path
25
+ end
26
+
27
+ @@strings = YAML.safe_load_file(File.expand_path("../strings/strings.yml", __FILE__))
28
+ def self.strings
29
+ @@strings
30
+ end
31
+ S = ->(name) { @@strings.fetch(name.to_s) { raise "Unknown translation! Please file a bug!" } }
32
+ end
33
+
34
+ loader.eager_load
@@ -0,0 +1,7 @@
1
+ disclaimer: This is alpha software, it's probably broken. Please specify a billing limit for your API key to avoid unexpected costs
2
+ paste_key: "Paste your OpenAI API key and press enter: "
3
+ already_setup: |
4
+ Clai is already setup! To regenerate a new config run:
5
+ $ clai setup --force
6
+ no_clai_yml: No clai.yml config file found. Please run 'clai setup'
7
+ prompt: "Your prompt: "
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Niklas Häusele
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-07-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zeitwerk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.6.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.6'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.6.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: http
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 5.1.1
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '5.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 5.1.1
53
+ - !ruby/object:Gem::Dependency
54
+ name: thor
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.2'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.2.2
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.2'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.2.2
73
+ description: Your personal AI for the terminal
74
+ email:
75
+ - niklas.haeusele@hey.com
76
+ executables:
77
+ - clai
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - CONTRIBUTING.md
82
+ - LICENSE
83
+ - README.md
84
+ - Rakefile
85
+ - docs/chat.md
86
+ - docs/intro.md
87
+ - docs/personas.md
88
+ - docs/session.md
89
+ - docs/setup.md
90
+ - exe/clai
91
+ - lib/clai.rb
92
+ - lib/clai/cli.rb
93
+ - lib/clai/commands/chat.rb
94
+ - lib/clai/commands/session.rb
95
+ - lib/clai/commands/setup.rb
96
+ - lib/clai/config.rb
97
+ - lib/clai/http_client.rb
98
+ - lib/clai/version.rb
99
+ - lib/strings/strings.yml
100
+ homepage: https://github.com/codergeek121/clai
101
+ licenses:
102
+ - MIT
103
+ metadata:
104
+ homepage_uri: https://github.com/codergeek121/clai
105
+ source_code_uri: https://github.com/codergeek121/clai
106
+ post_install_message: " _____ _ _____ \n / ____| | / |_ _|\n|
107
+ | | | / | | \n| | | | / / | | \n| |____| |____ / ____
108
+ \ _| |_ \n _____|______/_/ ______|\n \nThank you for
109
+ using clai.\n\nclai is early alpha software, beware of unexpected costs.\nMake sure
110
+ to add a billing limit to your OpenAI api key.\n\nTo start using clai, run 'clai
111
+ setup'\n"
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: 2.6.0
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubygems_version: 3.4.16
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Your personal AI for the terminal
130
+ test_files: []