sawara 0.1.0 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 458a9864b8cbbd7d4122608612f9282b1ca6adbbdbbf431faa879215f01f41aa
4
- data.tar.gz: 14e1a18199efa5d09f82ebdb6ffc483ccf6cfcedc09c47c1b323bfc10a8f2cab
3
+ metadata.gz: 33c01867584475125d8830efce467704af83e4704ea7eefd8160f7884fd726fe
4
+ data.tar.gz: e5fa76b6f98772772bdaa27443c858acef19fad65c5cd7ad6fbf31331e771dc0
5
5
  SHA512:
6
- metadata.gz: 328d34fee8546615568397b8f0e99540eeb638cd565ca8168c50923e2a99f00908f1ada67ac74a01c652fdedb8f93c61a64e275413c93678d45b456571c13d8a
7
- data.tar.gz: 5dffa179894a0d330c05574f4af0c889c1ca23b2122138bcd4521e9ed2b3befdf898a0c6b865ddaae90ca2288221ad3c3ced6556025de07d0c9982d4d08d1487
6
+ metadata.gz: f38fcf4a2acff8d37828661a4dd5a44dd5f79ca357dca3fd9c22a19dff3e5dc526c99866351e15c00b21164382fc832014037a7c7b8cb4906faa12bbafb08ce4
7
+ data.tar.gz: 5cd70107c0d993abc2027478cccf5d3d293f087b1a6c10d6b7bb09aab5c50b58e71bd9961b3fa4999fabc4c912f8ccb1b1dca175a56ad167336b9cfe3a5e3aa8
data/.rubocop.yml CHANGED
@@ -15,3 +15,7 @@ Style/StringLiteralsInInterpolation:
15
15
 
16
16
  Layout/LineLength:
17
17
  Max: 120
18
+
19
+ Metrics/BlockLength:
20
+ Exclude:
21
+ - "spec/**/*.rb"
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)
@@ -27,7 +33,7 @@ module Sawara
27
33
  Hint 2: To exit this conversation, press "Ctrl + d" without any message.
28
34
  MSG
29
35
  puts
30
- puts 'C h a t G P T C L I'.center(80)
36
+ puts 'S a w a r a 🐟'.center(80)
31
37
  puts '*' * 80
32
38
  puts hints
33
39
  puts '*' * 80
@@ -35,16 +41,23 @@ module Sawara
35
41
 
36
42
  def await_user_content
37
43
  puts
38
- print 'You: '
39
- content = $stdin.readlines.join
44
+ puts 'You:'
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
- print 'Vanilla: '
47
- content = client.fetch(@messages).sub!(/^\R/, '')
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.0'
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,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sawara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
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
- description: Use ChatGPT in Terminal via OpenAI API.
13
+ description: Sawara is a comannd-line tool for using ChatGPT via OpenAI API.
14
14
  email:
15
15
  - yocajii@stone.sakura.ne.jp
16
16
  executables:
@@ -29,9 +29,12 @@ 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
39
  - sig/chatgpt_cli.rbs
37
40
  homepage: https://github.com/yocajii/sawara
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