sawara 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34bee8ad292b23c3f9263d82403ae7da181151e5bd066288c557804f11258882
4
- data.tar.gz: 7891f97562dbbc91fc25f26f9c8fa964248cfd72b0c469c17f1760219973e95b
3
+ metadata.gz: 33c01867584475125d8830efce467704af83e4704ea7eefd8160f7884fd726fe
4
+ data.tar.gz: e5fa76b6f98772772bdaa27443c858acef19fad65c5cd7ad6fbf31331e771dc0
5
5
  SHA512:
6
- metadata.gz: cc5e8e6127bdde3375dc1bf6955b6375937e9d98b202337ce6dbd1716ba404eecc21d89a23aaf4f0697e99622e0289543049c9454ce12c512e0fcd69022a645a
7
- data.tar.gz: 894d27aa3d9218981c6f18462f2a5741de837872ea075ddd807c56747f7a0b6c5f188c34ae221a76ccfae555ad4c40f42967556d2f3ec31939fc9748c1944d97
6
+ metadata.gz: f38fcf4a2acff8d37828661a4dd5a44dd5f79ca357dca3fd9c22a19dff3e5dc526c99866351e15c00b21164382fc832014037a7c7b8cb4906faa12bbafb08ce4
7
+ data.tar.gz: 5cd70107c0d993abc2027478cccf5d3d293f087b1a6c10d6b7bb09aab5c50b58e71bd9961b3fa4999fabc4c912f8ccb1b1dca175a56ad167336b9cfe3a5e3aa8
data/Gemfile CHANGED
@@ -10,3 +10,4 @@ gem 'rake'
10
10
  gem 'rspec'
11
11
  gem 'rubocop'
12
12
  gem 'ruby-openai'
13
+ gem 'thor'
data/Gemfile.lock CHANGED
@@ -45,6 +45,7 @@ GEM
45
45
  ruby-openai (3.5.0)
46
46
  httparty (>= 0.18.1)
47
47
  ruby-progressbar (1.13.0)
48
+ thor (1.2.1)
48
49
  unicode-display_width (2.4.2)
49
50
 
50
51
  PLATFORMS
@@ -56,6 +57,7 @@ DEPENDENCIES
56
57
  rspec
57
58
  rubocop
58
59
  ruby-openai
60
+ thor
59
61
 
60
62
  BUNDLED WITH
61
63
  2.4.7
data/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Sawara
2
2
 
3
- Sawara is a Ruby Gem for using ChatGPT in your terminal via OpenAI API.
3
+ ![image](https://user-images.githubusercontent.com/33394676/224897066-0429c135-c1b0-49e4-8027-b96ebea93c6d.png)
4
+
5
+ ## Overview
6
+
7
+ Sawara is a Ruby Gem and command-line interface (CLI) tool that enables you to use ChatGPT in your terminal via OpenAI API.
8
+
9
+ With Sawara, you can easily create and manage multiple bots with different prompts, and have natural language conversations with them through the CLI.
10
+
11
+ Of course, you can also start a conversation with a bot without setting a prompt in advance.
4
12
 
5
13
  ## Installation
6
14
 
@@ -9,15 +17,53 @@ Sawara is a Ruby Gem for using ChatGPT in your terminal via OpenAI API.
9
17
  ## Initial setup
10
18
 
11
19
  1. Get your API key from [https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys)
12
- 2. `$ sawara`
20
+ 2. `$ sawara call` or `$ sawara setkey`
13
21
  3. Enter your API Key.
14
22
 
15
23
  ## Usage
16
24
 
17
- Executing `sawara` initiates a conversation with ChatGPT.
18
- ```
19
- $ sawara
20
- ```
25
+ ### Call a bot
26
+
27
+ $ sawara call
28
+ $ sawara -c
29
+
30
+ Use the `sawara call` command to start a conversation.
31
+
32
+ ### Call a registered bot
33
+
34
+ $ sawara call <bot_id>
35
+ $ sawara -c <bot_id>
36
+
37
+ Use the `sawara call` command followed by the ID of the bot to start a conversation with the registered bot.
38
+
39
+ ### Add a bot
40
+
41
+ $ sawara add <bot_id>
42
+
43
+ Use the `sawara add` command followed by the ID of the bot you want to register. You'll be asked to provide a name and a prompt for the bot.
44
+
45
+ ### Delete a bot
46
+
47
+ $ sawara delete <bot_id>
48
+
49
+ Use the `sawara delete` command followed by the ID of the bot you want to remove.
50
+
51
+ ### List all bots
52
+
53
+ $ sawara list
54
+ $ sawara -l
55
+
56
+ Use the `sawara list` command to list all the bots you've registered.
57
+
58
+ ### Set or update your OpenAI API key
59
+
60
+ $ sawara setkey
61
+
62
+ Use the `sawara setkey` command to set or update your OpenAI API key.
63
+
64
+ ### Configuration file
65
+
66
+ Configuration data will be saved in `~/.sawara.yml`.
21
67
 
22
68
  ## Development
23
69
 
data/exe/sawara CHANGED
@@ -3,6 +3,4 @@
3
3
 
4
4
  require 'sawara'
5
5
 
6
- config = Sawara::Config.new
7
- client = Sawara::ChatClient.new(config)
8
- Sawara::Talk.new.start(client)
6
+ Sawara::CLI.start
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sawara
4
+ class ApiKey
5
+ def read
6
+ UserConfig.read['api_key']
7
+ end
8
+
9
+ def update
10
+ api_key = await_api_key
11
+ return if api_key.nil?
12
+
13
+ UserConfig.save('api_key', api_key)
14
+ end
15
+
16
+ private
17
+
18
+ def await_api_key
19
+ puts 'Please enter your OpenAI API Key🔑'
20
+ input = $stdin.gets
21
+ return if input.nil?
22
+
23
+ api_key = input.chomp
24
+ api_key.empty? ? await_api_key : api_key
25
+ end
26
+ end
27
+ end
data/lib/sawara/bot.rb ADDED
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'readline'
4
+
5
+ module Sawara
6
+ class Bot
7
+ def initialize
8
+ @bots = UserConfig.read['bots']
9
+ end
10
+
11
+ def list
12
+ puts
13
+ if @bots.empty?
14
+ puts 'No bots are registered🫥'
15
+ puts 'To register a bot, use `sawara add [NAME]`.'
16
+ else
17
+ @bots.each do |id, profile|
18
+ puts "#{profile['name']}(#{id}):"
19
+ puts " \"#{profile['prompt']}\""
20
+ end
21
+ end
22
+ end
23
+
24
+ def create(id)
25
+ return unless id_is_valid?(id)
26
+
27
+ name = await_name
28
+ return if name.nil?
29
+
30
+ prompt = await_prompt
31
+ return if prompt.empty?
32
+
33
+ @bots[id] = { 'name' => name, 'prompt' => prompt }
34
+ UserConfig.save('bots', @bots)
35
+ puts
36
+ puts "#{id}'s default prompt registration was successful🎉"
37
+ puts "To begin a conversation with #{id}, use `sawara -c #{id}`."
38
+ end
39
+
40
+ def find(id)
41
+ if exists?(id)
42
+ profile = @bots[id]
43
+ puts
44
+ puts "Loaded the bot \"#{profile['name']}\". The prompt is as follows:"
45
+ puts profile['prompt']
46
+ profile
47
+ else
48
+ puts "The bot has id \"#{id}\" does not exist in the registered bots."
49
+ end
50
+ end
51
+
52
+ def delete(id)
53
+ if exists?(id)
54
+ puts "⚠️Are you sure you want to delete #{id}? (y/n)"
55
+ return unless await_confirm
56
+
57
+ @bots.delete(id)
58
+ UserConfig.save('bots', @bots)
59
+ puts
60
+ puts "\"#{id}\" has been deleted."
61
+ else
62
+ puts "The bot has id \"#{id}\" does not exist in the registered bots."
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def await_name
69
+ puts
70
+ puts 'Please enter the name of your bot😉'
71
+ line = Readline.readline
72
+ return if line.nil?
73
+
74
+ name = line.chomp
75
+ name.empty? ? await_name : name
76
+ end
77
+
78
+ def await_prompt
79
+ puts
80
+ puts 'Please enter the prompt of your bot📜'
81
+ lines = []
82
+ while (line = Readline.readline)
83
+ lines << ("#{line}\n")
84
+ end
85
+ lines.join.gsub(/^\n+|\n+$/, '')
86
+ end
87
+
88
+ def await_confirm
89
+ line = Readline.readline
90
+ return if line.nil?
91
+
92
+ confirm = line.chomp.downcase
93
+ confirm == 'y'
94
+ end
95
+
96
+ def id_is_valid?(id)
97
+ if id.empty?
98
+ puts 'Bot id must have at least one character.'
99
+ false
100
+ elsif exists?(id)
101
+ puts "\"#{id}\" is already registered id."
102
+ false
103
+ else
104
+ true
105
+ end
106
+ end
107
+
108
+ def exists?(id)
109
+ @bots.key?(id)
110
+ end
111
+ end
112
+ end
@@ -4,8 +4,7 @@ require 'openai'
4
4
 
5
5
  module Sawara
6
6
  class ChatClient
7
- def initialize(user_config)
8
- api_key = user_config.api_key
7
+ def initialize(api_key)
9
8
  OpenAI.configure do |config|
10
9
  config.access_token = api_key
11
10
  end
@@ -16,7 +15,7 @@ module Sawara
16
15
  response = @openai_client.chat(
17
16
  parameters: { model: 'gpt-3.5-turbo', messages: }
18
17
  )
19
- response.dig('choices', 0, 'message', 'content')
18
+ response.dig('choices', 0, 'message', 'content').sub(/^\n*/, '')
20
19
  end
21
20
  end
22
21
  end
data/lib/sawara/cli.rb ADDED
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Sawara
6
+ class CLI < Thor
7
+ desc 'call [ID], -c [ID]',
8
+ 'Start a conversation (If [ID] is there, preload the prompt of the bot specified by [ID])'
9
+ map '-c' => :call
10
+ def call(id = nil)
11
+ client = launch_client
12
+ if id.nil?
13
+ Talk.new.start(client)
14
+ else
15
+ profile = Bot.new.find(id)
16
+ return if profile.nil?
17
+
18
+ talk = Talk.new(profile)
19
+ talk.start(client)
20
+ end
21
+ end
22
+
23
+ desc 'setkey', 'Register or update an API key for OpenAI API'
24
+ def setkey
25
+ ApiKey.new.update
26
+ end
27
+
28
+ desc 'add [ID]', 'Register a new bot with name and prompt'
29
+ def add(id)
30
+ Bot.new.create(id)
31
+ end
32
+
33
+ desc 'delete [ID]', 'Delete the bot specified by [ID]'
34
+ def delete(id)
35
+ Bot.new.delete(id)
36
+ end
37
+
38
+ desc 'list, -l', 'List all bots with their names and prompts'
39
+ map '-l' => :list
40
+ def list
41
+ Bot.new.list
42
+ end
43
+
44
+ desc 'version, -v', 'Print version'
45
+ map %w[-v --version] => :version
46
+ def version
47
+ puts VERSION
48
+ end
49
+
50
+ private
51
+
52
+ def launch_client
53
+ api_key = ApiKey.new
54
+ api_key.update if api_key.read.empty?
55
+ ChatClient.new(api_key.read)
56
+ end
57
+ end
58
+ end
data/lib/sawara/talk.rb CHANGED
@@ -1,11 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'byebug'
3
+ require 'readline'
4
4
 
5
5
  module Sawara
6
6
  class Talk
7
- def initialize
7
+ def initialize(profile = nil)
8
8
  @messages = []
9
+ if profile.nil?
10
+ @name = 'Sawara'
11
+ else
12
+ @name = profile['name']
13
+ @messages << { role: 'system', content: profile['prompt'] }
14
+ end
9
15
  end
10
16
 
11
17
  def start(client)
@@ -36,15 +42,22 @@ module Sawara
36
42
  def await_user_content
37
43
  puts
38
44
  puts 'You:'
39
- content = $stdin.readlines.join.sub(/\n*$/, '')
45
+
46
+ lines = []
47
+ while (line = Readline.readline)
48
+ lines << ("#{line}\n")
49
+ end
50
+
51
+ content = lines.join
40
52
  @messages << { role: 'user', content: }
41
53
  content
42
54
  end
43
55
 
44
56
  def await_assistant_content(client)
45
57
  puts
46
- puts 'Sawara:'
47
- content = client.fetch(@messages).sub(/^\n*/, '')
58
+ puts "#{@name}:"
59
+
60
+ content = client.fetch(@messages)
48
61
  @messages << { role: 'assistant', content: }
49
62
  puts content
50
63
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'yaml/store'
5
+
6
+ module Sawara
7
+ class UserConfig
8
+ CONFIG_PATH = "#{Dir.home}/.sawara.yml".freeze
9
+
10
+ class << self
11
+ def read
12
+ create_config_file unless File.exist?(CONFIG_PATH)
13
+ YAML.load_file(CONFIG_PATH)
14
+ end
15
+
16
+ def save(key, value)
17
+ store = YAML::Store.new(CONFIG_PATH)
18
+ store.transaction do
19
+ store[key] = value
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def create_config_file
26
+ File.new(CONFIG_PATH, 'w')
27
+ save('api_key', '')
28
+ save('bots', {})
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sawara
4
- VERSION = '0.1.1'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/sawara.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'sawara/api_key'
3
4
  require_relative 'sawara/chat_client'
4
- require_relative 'sawara/config'
5
+ require_relative 'sawara/cli'
6
+ require_relative 'sawara/bot'
7
+ require_relative 'sawara/user_config'
5
8
  require_relative 'sawara/talk'
6
9
  require_relative 'sawara/version'
7
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sawara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yocajii
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-07 00:00:00.000000000 Z
11
+ date: 2023-03-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Sawara is a comannd-line tool for using ChatGPT via OpenAI API.
14
14
  email:
@@ -29,11 +29,13 @@ files:
29
29
  - Rakefile
30
30
  - exe/sawara
31
31
  - lib/sawara.rb
32
+ - lib/sawara/api_key.rb
33
+ - lib/sawara/bot.rb
32
34
  - lib/sawara/chat_client.rb
33
- - lib/sawara/config.rb
35
+ - lib/sawara/cli.rb
34
36
  - lib/sawara/talk.rb
37
+ - lib/sawara/user_config.rb
35
38
  - lib/sawara/version.rb
36
- - sawara.gemspec
37
39
  - sig/chatgpt_cli.rbs
38
40
  homepage: https://github.com/yocajii/sawara
39
41
  licenses:
data/lib/sawara/config.rb DELETED
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'yaml'
4
- require 'yaml/store'
5
-
6
- module Sawara
7
- class Config
8
- CONFIG_PATH = "#{Dir.home}/.sawara".freeze
9
-
10
- def initialize
11
- unless File.exist?(CONFIG_PATH)
12
- File.new(CONFIG_PATH, 'w')
13
- update_config
14
- end
15
- @config = load_config
16
- end
17
-
18
- def api_key
19
- @config['api_key']
20
- end
21
-
22
- private
23
-
24
- def load_config
25
- YAML.load_file(CONFIG_PATH)
26
- end
27
-
28
- def update_config
29
- puts 'Enter your OpenAI API Key.'
30
- api_key = $stdin.gets
31
- store = YAML::Store.new(CONFIG_PATH)
32
- store.transaction do
33
- store['api_key'] = api_key
34
- end
35
- end
36
- end
37
- end
data/sawara.gemspec DELETED
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'lib/sawara/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'sawara'
7
- spec.version = Sawara::VERSION
8
- spec.authors = ['yocajii']
9
- spec.email = ['yocajii@stone.sakura.ne.jp']
10
-
11
- spec.summary = 'This is ChatGPT CLI.'
12
- spec.description = 'Sawara is a comannd-line tool for using ChatGPT via OpenAI API.'
13
-
14
- spec.homepage = 'https://github.com/yocajii/sawara'
15
- spec.license = 'MIT'
16
- spec.required_ruby_version = '>= 3.1.0'
17
-
18
- spec.metadata['homepage_uri'] = spec.homepage
19
- spec.metadata['source_code_uri'] = 'https://github.com/yocajii/sawara'
20
- spec.metadata['changelog_uri'] = 'https://github.com/yocajii/sawara/blob/main/CHANGELOG.md'
21
-
22
- # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- spec.files = Dir.chdir(__dir__) do
25
- `git ls-files -z`.split("\x0").reject do |f|
26
- (File.expand_path(f) == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
27
- end
28
- end
29
- spec.bindir = 'exe'
30
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
- spec.require_paths = ['lib']
32
- spec.metadata['rubygems_mfa_required'] = 'true'
33
-
34
- # Uncomment to register a new dependency of your gem
35
- # spec.add_dependency 'example-gem', '~> 1.0'
36
-
37
- # For more information and examples about making a new gem, check out our
38
- # guide at: https://bundler.io/guides/creating_gem.html
39
- end