jambots 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/CHANGELOG.md +12 -2
- data/Gemfile.lock +1 -1
- data/README.md +22 -1
- data/experiments/bot_with_option_references/README.mb +33 -0
- data/experiments/bot_with_option_references/dev +34 -0
- data/lib/jambots/bot.rb +31 -11
- data/lib/jambots/cli.rb +5 -0
- data/lib/jambots/controllers/chat_controller.rb +50 -13
- data/lib/jambots/conversation.rb +2 -1
- data/lib/jambots/{renderer.rb → renderers/cli_renderer.rb} +17 -12
- data/lib/jambots/renderers/minimal_renderer.rb +13 -0
- data/lib/jambots/version.rb +1 -1
- data/lib/jambots.rb +4 -2
- metadata +6 -6
- data/exe/bot_sample.yml +0 -4
- data/sig/jambots.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82318d110379c68c81efc2636e9275a8fd68acfaa6ee6417c11ca2d4a6dbb15c
|
4
|
+
data.tar.gz: b7ff01c3e5eef32670225e5a533a08b27822a0cd2eedb1393ccb85df3f320cec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ddf78bd4e22f86a2737ea4db1f5956ee3ec2361ea0b2ff38c5a56636eee17ef3301a82b5e23ff482f33a57c34823f8e574248964a3d7addd609fb8a145261148
|
7
|
+
data.tar.gz: 4e639e9e4b7ba7f2815912c5cb0fca65856a490eb6966e16afabb948329c8fc0bd84f76a693e29cc1188e6d31216b2534da0eb0a5ddb294cd823b9359b5c4d58
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
-
|
1
|
+
### Unreleased
|
2
2
|
|
3
|
-
-
|
3
|
+
### [0.2.0] - 2023-05-12
|
4
|
+
|
5
|
+
- Handle OpenAI errors [#6](https://github.com/artero/jambots/issues/6).
|
6
|
+
- Add directory for experiments and examples, and add example "Bot with option references for Ruby development" [#10](https://github.com/artero/jambots/pull/10).
|
7
|
+
- Add no_pretty renderer option to chat command [#9](https://github.com/artero/jambots/pull/9).
|
8
|
+
- Fix Readme.
|
9
|
+
- Remove unnecessary files.
|
10
|
+
|
11
|
+
### [0.1.3] - 2023-05-07
|
12
|
+
|
13
|
+
- Initial release.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Jambots CLI
|
2
2
|
|
3
|
+
:warning: **Important notice:** This gem is in an early stage of development. Changes in commands or class interfaces may be introduced in future versions. Use it at your own risk and make sure to stay up-to-date with updates. :warning:
|
4
|
+
|
3
5
|
Jambots is a command-line interface (CLI) tool for interacting with chatbots powered by OpenAI's GPT. It allows you to create new chatbots, manage chatbot conversations, and send messages to chatbots.
|
4
6
|
|
5
7
|
## Installation
|
@@ -22,6 +24,15 @@ Or install it yourself as:
|
|
22
24
|
$ gem install jambots
|
23
25
|
```
|
24
26
|
|
27
|
+
You'll need to create the environment variable OPENAI_API_KEY with the value of your OpenAI API Key.
|
28
|
+
|
29
|
+
For instance if you use bash:
|
30
|
+
|
31
|
+
```
|
32
|
+
echo 'export OPENAI_API_KEY="your_openai_api_key"' >> ~/.bashrc
|
33
|
+
source ~/.bashrc
|
34
|
+
```
|
35
|
+
|
25
36
|
## Usage
|
26
37
|
|
27
38
|
### initialize a jambots path
|
@@ -40,6 +51,8 @@ This command will generate a Jambots directory with the default bot directory na
|
|
40
51
|
|
41
52
|
By default, this command initializes a Jambots directory in the current directory. However, you can use the `--path` or `--globally` options to create it at different paths.
|
42
53
|
|
54
|
+
|
55
|
+
|
43
56
|
#### The Jambot path
|
44
57
|
|
45
58
|
When you execute the subcommands jambots new or jambots chat without the `--path` option, Jambots will check for the existence of the `./.jambots` directory, and if not found, it will check for `~/.jambots.`
|
@@ -56,6 +69,7 @@ Options:
|
|
56
69
|
- `--conversation` or `-c`: Name of the conversation key
|
57
70
|
- `--path` or `-p`: Path where the bot and the conversation directory are located (default: "./.jambots or it it doesn't exist ~/.jambots")
|
58
71
|
- `--last` or `-l`: Continue with the last conversation created
|
72
|
+
- `--no_pretty` or `-n`: Disables pretty formatting for the output
|
59
73
|
|
60
74
|
#### Conversation example
|
61
75
|
|
@@ -117,7 +131,7 @@ and a party animal. However, he occasionally shows a kinder and more compassiona
|
|
117
131
|
side. Make sure to respond as if you were Bender in his interactions, using his
|
118
132
|
characteristic tone and style."
|
119
133
|
|
120
|
-
Bot 'bender' created './.jambots/
|
134
|
+
Bot 'bender' created './.jambots/bender'
|
121
135
|
```
|
122
136
|
|
123
137
|
That command will create the bender bot, its directory has the following structure:
|
@@ -166,6 +180,13 @@ Now, if you don't mind, I got some partying to do.
|
|
166
180
|
20230501122918 ───────────────────────
|
167
181
|
```
|
168
182
|
|
183
|
+
## Experiments
|
184
|
+
|
185
|
+
In this directory, you can find examples of how to use Jambots and experiments that we consider attractive.
|
186
|
+
|
187
|
+
- [Bot with option references for Ruby development](experiments/bot_with_option_references)
|
188
|
+
|
189
|
+
|
169
190
|
## Contributing
|
170
191
|
|
171
192
|
Bug reports and pull requests are welcome on GitHub at https://github.com/artero/jambots.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Bot with option references for Ruby development.
|
2
|
+
|
3
|
+
This experiment is useful to use it in Ruby development. It covers creating a local bot with basic instructions and implementing a Jambots CLI executable with reference options to add files as context in the conversation.
|
4
|
+
|
5
|
+
1. Initialize a new Jambot named `dev`:
|
6
|
+
|
7
|
+
```
|
8
|
+
jambots init dev
|
9
|
+
```
|
10
|
+
|
11
|
+
2. Edit the `./.jambots/dev/bot.yml` file with your desired settings:
|
12
|
+
|
13
|
+
```
|
14
|
+
model: gpt-3.5-turbo
|
15
|
+
prompt: |-
|
16
|
+
You will help me with programming in general and Ruby in particular.
|
17
|
+
Give short, concise one-sentence answers if possible.
|
18
|
+
```
|
19
|
+
|
20
|
+
> Note: If you have access to gpt-4 beta, use it, the results are lot better 🙂.
|
21
|
+
|
22
|
+
3. Execute the `dev` script. This command runs `jambots chat -b dev` with the `--refs` option, creates a message in the conversation for each file with the file path and file content as a reference to the OpenAI Chat API:
|
23
|
+
|
24
|
+
```
|
25
|
+
./dev "Create tests in rspec for the class Jambots::Conversation" --refs lib/jambots/conversation.rb
|
26
|
+
```
|
27
|
+
|
28
|
+
Take in consideration that this option sends the file name as reference too. Some times the path offers to the model information.
|
29
|
+
|
30
|
+
### Tips
|
31
|
+
|
32
|
+
- You can use `--refs` to send example files. For instance, when creating a new spec file, you can send another project's spec file to help OpenAI Chat replicate its style.
|
33
|
+
- Keep in mind the size of the files you send as references, as it may significantly increase the number of tokens in the conversation. OpenAI Chat counts content from all messages to calculate tokens, and each model has a different token limit.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "jambots"
|
5
|
+
|
6
|
+
BASIC_ARGS = ["chat", "-b", "dev"]
|
7
|
+
|
8
|
+
class DevBot < Jambots::Cli
|
9
|
+
desc "chat MESSAGE", "Start a chat with the bot and send a message"
|
10
|
+
option :bot, aliases: "-b", desc: "Name of the bot"
|
11
|
+
option :conversation, aliases: "-c", desc: "Name of the conversation key"
|
12
|
+
option :path, aliases: "-p", desc: "Path where the bot and the conversation directory are located"
|
13
|
+
option :last, type: :boolean, aliases: "-l", desc: "Continue with the last conversation created"
|
14
|
+
option :no_pretty, type: :boolean, aliases: "-n", desc: "Disables pretty formatting"
|
15
|
+
option :refs, type: :array, desc: "Add reference messages from files"
|
16
|
+
def chat(query)
|
17
|
+
chat_controller = Jambots::Controllers::ChatController.new(options)
|
18
|
+
add_reference_messages(chat_controller.conversation, options)
|
19
|
+
chat_controller.chat(query)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def add_reference_messages(conversation, options = {})
|
25
|
+
return unless options[:refs]
|
26
|
+
|
27
|
+
options[:refs].each do |file_path|
|
28
|
+
file_content = File.read(file_path)
|
29
|
+
conversation.add_message("system", "#{file_path}\n---\n #{file_content}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
DevBot.start(BASIC_ARGS + ARGV)
|
data/lib/jambots/bot.rb
CHANGED
@@ -6,8 +6,6 @@ require "fileutils"
|
|
6
6
|
require "yaml"
|
7
7
|
|
8
8
|
module Jambots
|
9
|
-
class OpenAIMessageError < StandardError; end
|
10
|
-
|
11
9
|
class Bot
|
12
10
|
DEFAULT_MODEL = "gpt-3.5-turbo"
|
13
11
|
DEFAULT_GLOBAL_BOTS_DIR = "#{ENV["HOME"]}/.jambots"
|
@@ -48,17 +46,14 @@ module Jambots
|
|
48
46
|
def initialize(name, args = {})
|
49
47
|
@bot_dir = "#{find_path(args[:path])}/#{name}"
|
50
48
|
|
51
|
-
raise "Bot #{name} doesn't exist." unless File.exist?("#{bot_dir}/bot.yml")
|
52
|
-
|
53
|
-
# Load options from bot.yml file if it exists
|
54
49
|
bot_yml_path = "#{@bot_dir}/bot.yml"
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
50
|
+
|
51
|
+
bot_options = load_bot_options(bot_yml_path)
|
52
|
+
args = bot_options.merge(args)
|
59
53
|
|
60
54
|
openai_api_key = args[:openai_api_key] || ENV["OPENAI_API_KEY"]
|
61
|
-
|
55
|
+
request_timeout = args[:request_timeout]
|
56
|
+
@client = OpenAI::Client.new(access_token: openai_api_key, request_timeout: request_timeout)
|
62
57
|
|
63
58
|
@name = name
|
64
59
|
@model = args[:model] || DEFAULT_MODEL
|
@@ -78,7 +73,7 @@ module Jambots
|
|
78
73
|
|
79
74
|
message = response.dig("choices", 0, "message")
|
80
75
|
|
81
|
-
raise
|
76
|
+
raise ChatClientError, handle_error(response) if response["error"]
|
82
77
|
|
83
78
|
conversation.add_message("assistant", message["content"])
|
84
79
|
conversation.save
|
@@ -123,5 +118,30 @@ module Jambots
|
|
123
118
|
total_files = Dir.glob("#{@conversations_dir}/*").count
|
124
119
|
"#{total_files + 1}.yml"
|
125
120
|
end
|
121
|
+
|
122
|
+
def load_bot_options(bot_yml_path)
|
123
|
+
raise "Bot #{name} doesn't exist." unless File.exist?(bot_yml_path)
|
124
|
+
|
125
|
+
YAML.safe_load(File.read(bot_yml_path), permitted_classes: [Symbol], symbolize_names: true)
|
126
|
+
end
|
127
|
+
|
128
|
+
def handle_error(response)
|
129
|
+
if response.dig("error", "code") == "invalid_api_key"
|
130
|
+
<<~HEREDOC
|
131
|
+
Invalid OpenAI API key. Please set the OPENAI_API_KEY environment variable to your OpenAI API key.
|
132
|
+
You can find your API key at https://beta.openai.com/account/api-keys.
|
133
|
+
HEREDOC
|
134
|
+
elsif response.dig("error", "code") == "max_tokens"
|
135
|
+
<<~HEREDOC
|
136
|
+
The chat is too long and exceeds the maximum number of tokens. The chat is very long and exceeds the maximum number of tokens.
|
137
|
+
Check the limitations of the model "#{model}" https://platform.openai.com/docs/models/overview
|
138
|
+
HEREDOC
|
139
|
+
else
|
140
|
+
<<~HEREDOC
|
141
|
+
OpenAI error - #{response["error"]["message"]}.
|
142
|
+
#{response}
|
143
|
+
HEREDOC
|
144
|
+
end
|
145
|
+
end
|
126
146
|
end
|
127
147
|
end
|
data/lib/jambots/cli.rb
CHANGED
@@ -2,6 +2,10 @@ require "thor"
|
|
2
2
|
|
3
3
|
module Jambots
|
4
4
|
class Cli < Thor
|
5
|
+
def self.exit_on_failure?
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
5
9
|
DEFAULT_BOT = "jambot"
|
6
10
|
|
7
11
|
desc "init", "Initialize a jambots path"
|
@@ -17,6 +21,7 @@ module Jambots
|
|
17
21
|
option :conversation, aliases: "-c", desc: "Name of the conversation key"
|
18
22
|
option :path, aliases: "-p", desc: "Path where the bot and the conversation directory are located"
|
19
23
|
option :last, type: :boolean, aliases: "-l", desc: "Continue with the last conversation created"
|
24
|
+
option :no_pretty, type: :boolean, aliases: "-n", desc: "Disables pretty formatting"
|
20
25
|
def chat(query)
|
21
26
|
chat_controller = Controllers::ChatController.new(options)
|
22
27
|
chat_controller.chat(query)
|
@@ -3,27 +3,64 @@
|
|
3
3
|
module Jambots::Controllers
|
4
4
|
class ChatController
|
5
5
|
DEFAULT_BOT = "jambot"
|
6
|
+
DEFAULT_TIMEOUT = 240
|
7
|
+
|
8
|
+
attr_reader :bot, :conversation, :renderer
|
6
9
|
|
7
10
|
def initialize(options)
|
8
|
-
|
9
|
-
|
11
|
+
bot_name = bot_name(options)
|
12
|
+
bot_options = bot_options(options)
|
13
|
+
@bot = Jambots::Bot.new(bot_name, bot_options)
|
14
|
+
|
15
|
+
conversation_options = conversation_options(options)
|
16
|
+
@conversation = load_conversation(conversation_options)
|
17
|
+
|
18
|
+
@renderer = load_renderer(options)
|
10
19
|
end
|
11
20
|
|
12
21
|
def chat(query)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
22
|
+
renderer.render(conversation) do
|
23
|
+
bot.message(query, conversation)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def bot_name(options)
|
30
|
+
options[:bot] || DEFAULT_BOT
|
31
|
+
end
|
17
32
|
|
18
|
-
|
19
|
-
|
33
|
+
def bot_options(options)
|
34
|
+
bot_options = {}
|
35
|
+
bot_options[:path] = options[:path] if options[:path]
|
36
|
+
bot_options[:model] = options[:model] if options[:model]
|
37
|
+
bot_options[:prompt] = options[:prompt] if options[:prompt]
|
38
|
+
bot_options[:openai_api_key] = options[:openai_api_key] if options[:openai_api_key]
|
39
|
+
bot_options[:request_timeout] = options[:request_timeout] if options[:request_timeout]
|
20
40
|
|
21
|
-
|
41
|
+
bot_options
|
42
|
+
end
|
43
|
+
|
44
|
+
def conversation_options(options)
|
45
|
+
{
|
46
|
+
conversation: options[:conversation],
|
47
|
+
last: options[:last]
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_conversation(options)
|
52
|
+
last = options[:last]
|
53
|
+
previous_conversation = last ? bot.conversations.last : bot.load_conversation(options[:conversation])
|
54
|
+
|
55
|
+
previous_conversation || bot.new_conversation
|
56
|
+
end
|
22
57
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
58
|
+
def load_renderer(options)
|
59
|
+
if options[:no_pretty]
|
60
|
+
Jambots::Renderers::MinimalRenderer.new
|
61
|
+
else
|
62
|
+
Jambots::Renderers::CliRenderer.new
|
63
|
+
end
|
27
64
|
end
|
28
65
|
end
|
29
66
|
end
|
data/lib/jambots/conversation.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module Jambots
|
4
4
|
class Conversation
|
5
|
-
attr_accessor :messages, :file_name, :file_path
|
5
|
+
attr_accessor :messages, :file_name, :file_path, :key
|
6
6
|
|
7
7
|
def initialize(file_path)
|
8
8
|
@file_path = file_path
|
9
9
|
@file_name = File.basename(file_path)
|
10
|
+
@key = File.basename(file_name, File.extname(file_name))
|
10
11
|
@messages = load_messages
|
11
12
|
end
|
12
13
|
|
@@ -3,8 +3,23 @@
|
|
3
3
|
require "tty-spinner"
|
4
4
|
require "pastel"
|
5
5
|
|
6
|
-
module Jambots
|
7
|
-
class
|
6
|
+
module Jambots::Renderers
|
7
|
+
class CliRenderer
|
8
|
+
def render(conversation, &block)
|
9
|
+
spinner.auto_spin
|
10
|
+
message = block.call
|
11
|
+
spinner.success
|
12
|
+
print_line(role_header(message[:role]))
|
13
|
+
puts pastel.magenta(message[:content])
|
14
|
+
print_line("#{conversation.key} ")
|
15
|
+
rescue Jambots::ChatClientError => e
|
16
|
+
spinner.success
|
17
|
+
warn "ERROR: #{e.message}"
|
18
|
+
exit(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
8
23
|
def spinner
|
9
24
|
@spinner ||= TTY::Spinner.new(
|
10
25
|
"(🤖) [#{pastel.green(":spinner")}] ",
|
@@ -17,16 +32,6 @@ module Jambots
|
|
17
32
|
@pastel ||= Pastel.new
|
18
33
|
end
|
19
34
|
|
20
|
-
def render(message, conversation)
|
21
|
-
print_line(role_header(message[:role]))
|
22
|
-
puts pastel.magenta(message[:content])
|
23
|
-
file_name = conversation.file_name
|
24
|
-
conversation_name = File.basename(file_name, File.extname(file_name))
|
25
|
-
print_line("#{conversation_name} ")
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
35
|
def role_header(rol)
|
31
36
|
case rol.to_sym
|
32
37
|
when :system
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jambots::Renderers
|
4
|
+
class MinimalRenderer
|
5
|
+
def render(conversation, &block)
|
6
|
+
message = block.call
|
7
|
+
puts message[:content]
|
8
|
+
rescue Jambots::ChatClientError => e
|
9
|
+
warn "ERROR: #{e.message}"
|
10
|
+
exit(1)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/jambots/version.rb
CHANGED
data/lib/jambots.rb
CHANGED
@@ -3,13 +3,15 @@
|
|
3
3
|
require_relative "jambots/version"
|
4
4
|
require_relative "jambots/bot"
|
5
5
|
require_relative "jambots/conversation"
|
6
|
-
require_relative "jambots/renderer"
|
7
6
|
require_relative "jambots/cli"
|
7
|
+
require_relative "jambots/renderers/cli_renderer"
|
8
|
+
require_relative "jambots/renderers/minimal_renderer"
|
8
9
|
require_relative "jambots/controllers/init_controller"
|
9
10
|
require_relative "jambots/controllers/chat_controller"
|
10
11
|
require_relative "jambots/controllers/new_controller"
|
11
12
|
|
12
13
|
module Jambots
|
13
14
|
class Error < StandardError; end
|
14
|
-
|
15
|
+
|
16
|
+
class ChatClientError < Error; end
|
15
17
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jambots
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Artero
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-openai
|
@@ -100,7 +100,6 @@ description: " Jambots is a command-line interface (CLI) tool for interacting
|
|
100
100
|
email:
|
101
101
|
- juan.artero@marsbased.com
|
102
102
|
executables:
|
103
|
-
- bot_sample.yml
|
104
103
|
- jambots
|
105
104
|
extensions: []
|
106
105
|
extra_rdoc_files: []
|
@@ -114,8 +113,9 @@ files:
|
|
114
113
|
- LICENSE.txt
|
115
114
|
- README.md
|
116
115
|
- Rakefile
|
117
|
-
- exe/bot_sample.yml
|
118
116
|
- exe/jambots
|
117
|
+
- experiments/bot_with_option_references/README.mb
|
118
|
+
- experiments/bot_with_option_references/dev
|
119
119
|
- lib/jambots.rb
|
120
120
|
- lib/jambots/bot.rb
|
121
121
|
- lib/jambots/cli.rb
|
@@ -123,9 +123,9 @@ files:
|
|
123
123
|
- lib/jambots/controllers/init_controller.rb
|
124
124
|
- lib/jambots/controllers/new_controller.rb
|
125
125
|
- lib/jambots/conversation.rb
|
126
|
-
- lib/jambots/
|
126
|
+
- lib/jambots/renderers/cli_renderer.rb
|
127
|
+
- lib/jambots/renderers/minimal_renderer.rb
|
127
128
|
- lib/jambots/version.rb
|
128
|
-
- sig/jambots.rbs
|
129
129
|
homepage: https://github.com/artero/jambots
|
130
130
|
licenses:
|
131
131
|
- MIT
|
data/exe/bot_sample.yml
DELETED
data/sig/jambots.rbs
DELETED