nano-bots 0.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/.gitignore +2 -0
- data/.rspec +1 -0
- data/.rubocop.yml +9 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +88 -0
- data/LICENSE +21 -0
- data/README.md +173 -0
- data/bin/rnb +4 -0
- data/components/provider.rb +20 -0
- data/components/providers/base.rb +15 -0
- data/components/providers/openai.rb +79 -0
- data/components/storage.rb +76 -0
- data/controllers/instance.rb +65 -0
- data/controllers/interfaces/cli.rb +47 -0
- data/controllers/interfaces/repl.rb +67 -0
- data/controllers/session.rb +114 -0
- data/logic/helpers/hash.rb +35 -0
- data/nano-bots.gemspec +42 -0
- data/ports/dsl/nano-bots/cli.rb +5 -0
- data/ports/dsl/nano-bots.rb +25 -0
- data/static/gem.rb +15 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: eb32dfa429c178b7af6c3f9a4c8046846ab9ae894c94bbf2038644ceef9c2368
|
4
|
+
data.tar.gz: 21e37c25c085c27f0f0357f8bb878cba2a366641f9d4649dc25536bcd51afc28
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0a4a1f1a7f33eb6da088b8418cd0d4da61d8e6890ee8b6a0276330217164966b7266c032eabf3e3454e7879bd5e525b47cc99eff40259b7a1a9f9cf86a72db4f
|
7
|
+
data.tar.gz: 2098e8e5da98da2499ea4267e70be61ba9a06feb5c3fb6375d8ff05dbd01ae72134d34e5205884af80f3c4029c32dc09fec3c91b744ee6f61b05db8a5309b8ec
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
nano-bots (0.0.1)
|
5
|
+
babosa (~> 2.0)
|
6
|
+
dotenv (~> 2.8, >= 2.8.1)
|
7
|
+
faraday (~> 2.7, >= 2.7.4)
|
8
|
+
pry (~> 0.14.2)
|
9
|
+
rainbow (~> 3.1, >= 3.1.1)
|
10
|
+
ruby-openai (~> 4.0)
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
ast (2.4.2)
|
16
|
+
babosa (2.0.0)
|
17
|
+
coderay (1.1.3)
|
18
|
+
diff-lcs (1.5.0)
|
19
|
+
dotenv (2.8.1)
|
20
|
+
faraday (2.7.4)
|
21
|
+
faraday-net_http (>= 2.0, < 3.1)
|
22
|
+
ruby2_keywords (>= 0.0.4)
|
23
|
+
faraday-multipart (1.0.4)
|
24
|
+
multipart-post (~> 2)
|
25
|
+
faraday-net_http (3.0.2)
|
26
|
+
json (2.6.3)
|
27
|
+
method_source (1.0.0)
|
28
|
+
multipart-post (2.3.0)
|
29
|
+
parallel (1.23.0)
|
30
|
+
parser (3.2.2.1)
|
31
|
+
ast (~> 2.4.1)
|
32
|
+
pry (0.14.2)
|
33
|
+
coderay (~> 1.1)
|
34
|
+
method_source (~> 1.0)
|
35
|
+
rainbow (3.1.1)
|
36
|
+
regexp_parser (2.8.0)
|
37
|
+
rexml (3.2.5)
|
38
|
+
rspec (3.12.0)
|
39
|
+
rspec-core (~> 3.12.0)
|
40
|
+
rspec-expectations (~> 3.12.0)
|
41
|
+
rspec-mocks (~> 3.12.0)
|
42
|
+
rspec-core (3.12.2)
|
43
|
+
rspec-support (~> 3.12.0)
|
44
|
+
rspec-expectations (3.12.3)
|
45
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
46
|
+
rspec-support (~> 3.12.0)
|
47
|
+
rspec-mocks (3.12.5)
|
48
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
49
|
+
rspec-support (~> 3.12.0)
|
50
|
+
rspec-support (3.12.0)
|
51
|
+
rubocop (1.50.2)
|
52
|
+
json (~> 2.3)
|
53
|
+
parallel (~> 1.10)
|
54
|
+
parser (>= 3.2.0.0)
|
55
|
+
rainbow (>= 2.2.2, < 4.0)
|
56
|
+
regexp_parser (>= 1.8, < 3.0)
|
57
|
+
rexml (>= 3.2.5, < 4.0)
|
58
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
59
|
+
ruby-progressbar (~> 1.7)
|
60
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
61
|
+
rubocop-ast (1.28.1)
|
62
|
+
parser (>= 3.2.1.0)
|
63
|
+
rubocop-capybara (2.18.0)
|
64
|
+
rubocop (~> 1.41)
|
65
|
+
rubocop-factory_bot (2.22.0)
|
66
|
+
rubocop (~> 1.33)
|
67
|
+
rubocop-rspec (2.22.0)
|
68
|
+
rubocop (~> 1.33)
|
69
|
+
rubocop-capybara (~> 2.17)
|
70
|
+
rubocop-factory_bot (~> 2.22)
|
71
|
+
ruby-openai (4.0.0)
|
72
|
+
faraday (>= 1)
|
73
|
+
faraday-multipart (>= 1)
|
74
|
+
ruby-progressbar (1.13.0)
|
75
|
+
ruby2_keywords (0.0.5)
|
76
|
+
unicode-display_width (2.4.2)
|
77
|
+
|
78
|
+
PLATFORMS
|
79
|
+
x86_64-linux
|
80
|
+
|
81
|
+
DEPENDENCIES
|
82
|
+
nano-bots!
|
83
|
+
rspec (~> 3.12)
|
84
|
+
rubocop (~> 1.47)
|
85
|
+
rubocop-rspec (~> 2.22)
|
86
|
+
|
87
|
+
BUNDLED WITH
|
88
|
+
2.4.13
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 icebaker
|
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,173 @@
|
|
1
|
+
# Nano Bots 💎 🤖
|
2
|
+
|
3
|
+
A Ruby implementation of the [Nano Bots](https://github.com/icebaker/nano-bots) specification.
|
4
|
+
|
5
|
+

|
6
|
+
_Image artificially created by Midjourney through a prompt generated by a Nano Bot specialized in Midjourney._
|
7
|
+
|
8
|
+
https://user-images.githubusercontent.com/113217272/237840989-1e29a5cc-6644-48d0-87b4-62798dc6ebd3.mp4
|
9
|
+
|
10
|
+
- [Setup](#setup)
|
11
|
+
- [Usage](#usage)
|
12
|
+
- [Command Line](#command-line)
|
13
|
+
- [Library](#library)
|
14
|
+
- [Cartridges](#cartridges)
|
15
|
+
- [Development](#development)
|
16
|
+
- [Publish to RubyGems](#publish-to-rubygems)
|
17
|
+
|
18
|
+
## Setup
|
19
|
+
|
20
|
+
For a system usage:
|
21
|
+
|
22
|
+
```sh
|
23
|
+
gem install nano-bots -v 0.0.1
|
24
|
+
```
|
25
|
+
|
26
|
+
To use it in a project, add it to your `Gemfile`:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'nano-bots', '~> 0.0.1'
|
30
|
+
```
|
31
|
+
|
32
|
+
```sh
|
33
|
+
bundle install
|
34
|
+
```
|
35
|
+
|
36
|
+
For credentials and configurations, relevant environment variables can be set in your `.bashrc`, `.zshrc`, or equivalent files, as well as in your Docker Container or System Environment. Example:
|
37
|
+
|
38
|
+
```sh
|
39
|
+
export OPENAI_API_ADDRESS=https://api.openai.com
|
40
|
+
export OPENAI_API_ACCESS_TOKEN=your-token
|
41
|
+
export OPENAI_API_USER_IDENTIFIER=your-user
|
42
|
+
|
43
|
+
export NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
|
44
|
+
export NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
|
45
|
+
```
|
46
|
+
|
47
|
+
Alternatively, if your current directory has a `.env` file with the environment variables, they will be automatically loaded.
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
### Command Line
|
52
|
+
|
53
|
+
After installing the gem, the `rnb` binary command will be available for your project or system.
|
54
|
+
|
55
|
+
Examples of usage:
|
56
|
+
|
57
|
+
```bash
|
58
|
+
rnb to-en-us-translator.yml - eval "Salut, comment ça va?"
|
59
|
+
# => Hello, how are you doing?
|
60
|
+
|
61
|
+
rnb midjourney.yml - eval "happy and friendly cyberpunk robot"
|
62
|
+
# => The robot exploring a bustling city, surrounded by neon lights
|
63
|
+
# and high-rise buildings. The prompt should include colorful
|
64
|
+
# lighting and a sense of excitement in the facial expression.
|
65
|
+
|
66
|
+
rnb lisp.yml - eval "(+ 1 2)"
|
67
|
+
# => 3
|
68
|
+
|
69
|
+
cat article.txt |
|
70
|
+
rnb to-en-us-translator.yml - eval |
|
71
|
+
rnb summarizer.yml - eval
|
72
|
+
# -> LLM stands for Large Language Model, which refers to an
|
73
|
+
# artificial intelligence algorithm capable of processing
|
74
|
+
# and understanding vast amounts of natural language data,
|
75
|
+
# allowing it to generate human-like responses and perform
|
76
|
+
# a range of language-related tasks.
|
77
|
+
```
|
78
|
+
|
79
|
+
```bash
|
80
|
+
rnb assistant.yml - repl
|
81
|
+
```
|
82
|
+
|
83
|
+
All of the commands above are stateless. If you want to preserve the history of your interactions, replace the `-` with a state key. You can use a simple key, such as your username, or a randomly generated one:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
require 'securerandom'
|
87
|
+
|
88
|
+
SecureRandom.hex # => 6ea6c43c42a1c076b1e3c36fa349ac2c
|
89
|
+
```
|
90
|
+
|
91
|
+
```bash
|
92
|
+
rnb assistant.yml your-user eval "Salut, comment ça va?"
|
93
|
+
rnb assistant.yml your-user repl
|
94
|
+
|
95
|
+
rnb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c eval "Salut, comment ça va?"
|
96
|
+
rnb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c repl
|
97
|
+
```
|
98
|
+
|
99
|
+
### Library
|
100
|
+
|
101
|
+
To use it as a library:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
require 'nano-bots/cli' # Equivalent to the `rnb` command.
|
105
|
+
```
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
require 'nano-bots'
|
109
|
+
|
110
|
+
NanoBot.cli # Equivalent to the `rnb` command.
|
111
|
+
|
112
|
+
NanoBot.repl(cartridge: 'cartridge.yml') # Starts a new REPL.
|
113
|
+
|
114
|
+
bot = NanoBot.new(cartridge: 'cartridge.yml')
|
115
|
+
|
116
|
+
bot.eval('Hello')
|
117
|
+
|
118
|
+
bot.repl # Starts a new REPL.
|
119
|
+
|
120
|
+
NanoBot.repl(cartridge: 'cartridge.yml', state: '6ea6c43c42a1c076b1e3c36fa349ac2c')
|
121
|
+
|
122
|
+
bot = NanoBot.new(cartridge: 'cartridge.yml', state: '6ea6c43c42a1c076b1e3c36fa349ac2c')
|
123
|
+
```
|
124
|
+
|
125
|
+
## Cartridges
|
126
|
+
|
127
|
+
Here's what a Nano Bot Cartridge looks like:
|
128
|
+
|
129
|
+
```yaml
|
130
|
+
---
|
131
|
+
name: Assistant
|
132
|
+
version: 0.0.1
|
133
|
+
|
134
|
+
behaviors:
|
135
|
+
interaction:
|
136
|
+
directive: You are a helpful assistant.
|
137
|
+
|
138
|
+
interfaces:
|
139
|
+
repl:
|
140
|
+
prompt:
|
141
|
+
- text: '🤖'
|
142
|
+
- text: '> '
|
143
|
+
color: blue
|
144
|
+
|
145
|
+
provider:
|
146
|
+
name: openai
|
147
|
+
settings:
|
148
|
+
model: gpt-3.5-turbo
|
149
|
+
credentials:
|
150
|
+
address: ENV/OPENAI_API_ADDRESS
|
151
|
+
access-token: ENV/OPENAI_API_ACCESS_TOKEN
|
152
|
+
user-identifier: ENV/OPENAI_API_USER_IDENTIFIER
|
153
|
+
```
|
154
|
+
|
155
|
+
Check the Nano Bots specification to learn more about [how to build cartridges](https://icebaker.github.io/nano-bots/#/README?id=cartridges).
|
156
|
+
|
157
|
+
## Development
|
158
|
+
|
159
|
+
```bash
|
160
|
+
bundle
|
161
|
+
rubocop -A
|
162
|
+
rspec
|
163
|
+
```
|
164
|
+
|
165
|
+
### Publish to RubyGems
|
166
|
+
|
167
|
+
```bash
|
168
|
+
gem build nano-bots.gemspec
|
169
|
+
|
170
|
+
gem signin
|
171
|
+
|
172
|
+
gem push nano-bots-0.0.1.gem
|
173
|
+
```
|
data/bin/rnb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openai'
|
4
|
+
|
5
|
+
require_relative './providers/openai'
|
6
|
+
|
7
|
+
module NanoBot
|
8
|
+
module Components
|
9
|
+
class Provider
|
10
|
+
def self.new(provider)
|
11
|
+
case provider[:name]
|
12
|
+
when 'openai'
|
13
|
+
Providers::OpenAI.new(provider[:settings])
|
14
|
+
else
|
15
|
+
raise "Unsupported provider #{provider[:name]}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openai'
|
4
|
+
|
5
|
+
module NanoBot
|
6
|
+
module Components
|
7
|
+
module Providers
|
8
|
+
class Base
|
9
|
+
def evaluate(_payload)
|
10
|
+
raise NoMethodError, "The 'evaluate' method is not implemented for the current provider."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openai'
|
4
|
+
|
5
|
+
require_relative './base'
|
6
|
+
|
7
|
+
module NanoBot
|
8
|
+
module Components
|
9
|
+
module Providers
|
10
|
+
class OpenAI < Base
|
11
|
+
CHAT_SETTINGS = %i[
|
12
|
+
model stream temperature top_p n stop max_tokens
|
13
|
+
presence_penalty frequency_penalty logit_bias
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
attr_reader :settings
|
17
|
+
|
18
|
+
def initialize(settings)
|
19
|
+
@settings = settings
|
20
|
+
|
21
|
+
@client = ::OpenAI::Client.new(
|
22
|
+
uri_base: "#{@settings[:credentials][:address].sub(%r{/$}, '')}/",
|
23
|
+
access_token: @settings[:credentials][:'access-token']
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def evaluate(input, &block)
|
28
|
+
messages = input[:history].map do |event|
|
29
|
+
{ role: event[:who] == 'user' ? 'user' : 'assistant',
|
30
|
+
content: event[:message] }
|
31
|
+
end
|
32
|
+
|
33
|
+
%i[instruction backdrop directive].each do |key|
|
34
|
+
next unless input[:behavior][key]
|
35
|
+
|
36
|
+
messages.prepend(
|
37
|
+
{ role: key == :directive ? 'system' : 'user',
|
38
|
+
content: input[:behavior][key] }
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
payload = {
|
43
|
+
model: @settings[:model],
|
44
|
+
user: @settings[:credentials][:'user-identifier'],
|
45
|
+
messages:
|
46
|
+
}
|
47
|
+
|
48
|
+
CHAT_SETTINGS.each do |key|
|
49
|
+
payload[key] = @settings[key] if @settings.key?(key)
|
50
|
+
end
|
51
|
+
|
52
|
+
payload.delete(:logit_bias) if payload.key?(:logit_bias) && payload[:logit_bias].nil?
|
53
|
+
|
54
|
+
if @settings[:stream] && input[:interface][:stream]
|
55
|
+
content = ''
|
56
|
+
|
57
|
+
payload[:stream] = proc do |chunk, _bytesize|
|
58
|
+
partial = chunk.dig('choices', 0, 'delta', 'content')
|
59
|
+
if partial
|
60
|
+
content += partial
|
61
|
+
block.call({ who: 'AI', message: partial }, false)
|
62
|
+
end
|
63
|
+
|
64
|
+
block.call({ who: 'AI', message: content }, true) if chunk.dig('choices', 0, 'finish_reason')
|
65
|
+
end
|
66
|
+
|
67
|
+
@client.chat(parameters: payload)
|
68
|
+
else
|
69
|
+
result = @client.chat(parameters: payload)
|
70
|
+
|
71
|
+
raise StandardError, result['error'] if result['error']
|
72
|
+
|
73
|
+
block.call({ who: 'AI', message: result.dig('choices', 0, 'message', 'content') }, true)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'babosa'
|
4
|
+
|
5
|
+
require_relative '../logic/helpers/hash'
|
6
|
+
|
7
|
+
module NanoBot
|
8
|
+
module Components
|
9
|
+
class Storage
|
10
|
+
def self.build_path_and_ensure_state_file!(key, cartridge)
|
11
|
+
path = [
|
12
|
+
Logic::Helpers::Hash.fetch(cartridge, %i[state directory]),
|
13
|
+
ENV.fetch('NANO_BOTS_STATE_DIRECTORY', nil)
|
14
|
+
].find do |candidate|
|
15
|
+
!candidate.nil? && !candidate.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
path = "#{user_home!.sub(%r{/$}, '')}/.local/state/nano-bots" if path.nil?
|
19
|
+
|
20
|
+
path = "#{path.sub(%r{/$}, '')}/nano-bots-rb/#{cartridge[:name].to_slug.normalize}"
|
21
|
+
path = "#{path}/#{cartridge[:version].to_slug.normalize}/#{key.to_slug.normalize}"
|
22
|
+
path = "#{path}/state.json"
|
23
|
+
|
24
|
+
FileUtils.mkdir_p(File.dirname(path))
|
25
|
+
|
26
|
+
File.write(path, JSON.generate({ key:, history: [] })) unless File.exist?(path)
|
27
|
+
|
28
|
+
path
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.cartridge_path(path)
|
32
|
+
partial = File.join(File.dirname(path), File.basename(path, File.extname(path)))
|
33
|
+
|
34
|
+
candidates = [
|
35
|
+
path,
|
36
|
+
"#{partial}.yml",
|
37
|
+
"#{partial}.yaml"
|
38
|
+
]
|
39
|
+
|
40
|
+
unless ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY', nil).nil?
|
41
|
+
directory = ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY').sub(%r{/$}, '')
|
42
|
+
|
43
|
+
partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial)))
|
44
|
+
|
45
|
+
partial = path.sub(%r{^\.?/}, '')
|
46
|
+
|
47
|
+
candidates << "#{directory}/#{partial}"
|
48
|
+
candidates << "#{directory}/#{partial}.yml"
|
49
|
+
candidates << "#{directory}/#{partial}.yaml"
|
50
|
+
end
|
51
|
+
|
52
|
+
directory = "#{user_home!.sub(%r{/$}, '')}/.local/share/nano-bots/cartridges"
|
53
|
+
|
54
|
+
partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial)))
|
55
|
+
|
56
|
+
partial = path.sub(%r{^\.?/}, '')
|
57
|
+
|
58
|
+
candidates << "#{directory}/#{partial}"
|
59
|
+
candidates << "#{directory}/#{partial}.yml"
|
60
|
+
candidates << "#{directory}/#{partial}.yaml"
|
61
|
+
|
62
|
+
candidates = candidates.uniq
|
63
|
+
|
64
|
+
candidates.find do |candidate|
|
65
|
+
File.exist?(candidate) && File.file?(candidate)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.user_home!
|
70
|
+
[Dir.home, `echo ~`.strip, '~'].find do |candidate|
|
71
|
+
!candidate.nil? && !candidate.empty?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require_relative '../logic/helpers/hash'
|
6
|
+
require_relative '../components/provider'
|
7
|
+
require_relative '../components/storage'
|
8
|
+
require_relative './interfaces/repl'
|
9
|
+
require_relative './session'
|
10
|
+
|
11
|
+
module NanoBot
|
12
|
+
module Controllers
|
13
|
+
class Instance
|
14
|
+
def initialize(cartridge_path:, state: nil)
|
15
|
+
load_cartridge!(cartridge_path)
|
16
|
+
|
17
|
+
provider = Components::Provider.new(@cartridge[:provider])
|
18
|
+
|
19
|
+
@session = Session.new(provider:, cartridge: @cartridge, state:)
|
20
|
+
end
|
21
|
+
|
22
|
+
def debug
|
23
|
+
@session.debug
|
24
|
+
end
|
25
|
+
|
26
|
+
def eval(input)
|
27
|
+
@session.evaluate_and_print(input, mode: 'eval')
|
28
|
+
end
|
29
|
+
|
30
|
+
def repl
|
31
|
+
Interfaces::REPL.start(@cartridge, @session)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def load_cartridge!(path)
|
37
|
+
@cartridge = Logic::Helpers::Hash.symbolize_keys(
|
38
|
+
YAML.safe_load(
|
39
|
+
File.read(Components::Storage.cartridge_path(path)),
|
40
|
+
permitted_classes: [Symbol]
|
41
|
+
)
|
42
|
+
)
|
43
|
+
|
44
|
+
inject_environment_variables!(@cartridge)
|
45
|
+
end
|
46
|
+
|
47
|
+
def inject_environment_variables!(node)
|
48
|
+
case node
|
49
|
+
when Hash
|
50
|
+
node.each do |key, value|
|
51
|
+
node[key] = inject_environment_variables!(value)
|
52
|
+
end
|
53
|
+
when Array
|
54
|
+
node.each_with_index do |value, index|
|
55
|
+
node[index] = inject_environment_variables!(value)
|
56
|
+
end
|
57
|
+
when String
|
58
|
+
node.start_with?('ENV') ? ENV.fetch(node.sub(/^ENV./, ''), nil) : node
|
59
|
+
else
|
60
|
+
node
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../instance'
|
4
|
+
require_relative '../../static/gem'
|
5
|
+
|
6
|
+
module NanoBot
|
7
|
+
module Controllers
|
8
|
+
module Interfaces
|
9
|
+
module CLI
|
10
|
+
def self.handle!
|
11
|
+
case ARGV[0]
|
12
|
+
when 'version'
|
13
|
+
puts NanoBot::GEM[:version]
|
14
|
+
exit
|
15
|
+
when 'help', '', nil
|
16
|
+
puts "Ruby Nano Bots #{NanoBot::GEM[:version]}"
|
17
|
+
puts ' rnb cartridge.yml - eval "Hello"'
|
18
|
+
puts ' rnb cartridge.yml - repl'
|
19
|
+
puts ' rnb cartridge.yml - debug'
|
20
|
+
puts ' rnb cartridge.yml STATE-KEY eval "Hello"'
|
21
|
+
puts ' rnb cartridge.yml STATE-KEY repl'
|
22
|
+
puts ' rnb cartridge.yml STATE-KEY debug'
|
23
|
+
puts ' rnb version'
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
params = { cartridge_path: ARGV[0], state: ARGV[1], command: ARGV[2] }
|
28
|
+
|
29
|
+
bot = Instance.new(cartridge_path: params[:cartridge_path], state: params[:state])
|
30
|
+
|
31
|
+
case params[:command]
|
32
|
+
when 'eval'
|
33
|
+
params[:input] = ARGV[3..]&.join(' ')
|
34
|
+
params[:input] = $stdin.read.chomp if params[:input].nil? || params[:input].empty?
|
35
|
+
bot.eval(params[:input])
|
36
|
+
when 'repl'
|
37
|
+
bot.repl
|
38
|
+
when 'debug'
|
39
|
+
bot.debug
|
40
|
+
else
|
41
|
+
raise "TODO: [#{params[:command]}]"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pry'
|
4
|
+
require 'rainbow'
|
5
|
+
|
6
|
+
require_relative '../../logic/helpers/hash'
|
7
|
+
|
8
|
+
module NanoBot
|
9
|
+
module Controllers
|
10
|
+
module Interfaces
|
11
|
+
module REPL
|
12
|
+
def self.start(cartridge, session)
|
13
|
+
if Logic::Helpers::Hash.fetch(
|
14
|
+
cartridge, %i[interfaces repl prefix]
|
15
|
+
)
|
16
|
+
session.print(Logic::Helpers::Hash.fetch(cartridge,
|
17
|
+
%i[interfaces repl prefix]))
|
18
|
+
end
|
19
|
+
|
20
|
+
session.boot(mode: 'repl')
|
21
|
+
|
22
|
+
session.print(Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl postfix]) || "\n")
|
23
|
+
|
24
|
+
session.flush
|
25
|
+
|
26
|
+
prompt = build_prompt(cartridge[:interfaces][:repl][:prompt])
|
27
|
+
|
28
|
+
Pry.config.prompt = Pry::Prompt.new(
|
29
|
+
'REPL',
|
30
|
+
'REPL Prompt',
|
31
|
+
[proc { prompt }, proc { 'MISSING INPUT' }]
|
32
|
+
)
|
33
|
+
|
34
|
+
Pry.commands.block_command(/(.*)/, 'handler') do |line|
|
35
|
+
if Logic::Helpers::Hash.fetch(
|
36
|
+
cartridge, %i[interfaces repl prefix]
|
37
|
+
)
|
38
|
+
session.print(Logic::Helpers::Hash.fetch(
|
39
|
+
cartridge, %i[interfaces repl prefix]
|
40
|
+
))
|
41
|
+
end
|
42
|
+
|
43
|
+
session.evaluate_and_print(line, mode: 'repl')
|
44
|
+
session.print(Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl postfix]) || "\n")
|
45
|
+
session.flush
|
46
|
+
end
|
47
|
+
|
48
|
+
Pry.start
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.build_prompt(prompt)
|
52
|
+
result = ''
|
53
|
+
|
54
|
+
prompt.each do |partial|
|
55
|
+
result += if partial[:color]
|
56
|
+
Rainbow(partial[:text]).send(partial[:color])
|
57
|
+
else
|
58
|
+
partial[:text]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'babosa'
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
require_relative '../logic/helpers/hash'
|
8
|
+
require_relative '../components/storage'
|
9
|
+
|
10
|
+
module NanoBot
|
11
|
+
module Controllers
|
12
|
+
STREAM_TIMEOUT_IN_SECONDS = 5
|
13
|
+
|
14
|
+
class Session
|
15
|
+
def initialize(provider:, cartridge:, state: nil)
|
16
|
+
@provider = provider
|
17
|
+
@cartridge = cartridge
|
18
|
+
|
19
|
+
@output = $stdout
|
20
|
+
|
21
|
+
@stateless = state.nil? || state.strip == '-' || state.strip.empty?
|
22
|
+
|
23
|
+
if @stateless
|
24
|
+
@state = { history: [] }
|
25
|
+
else
|
26
|
+
@state_path = Components::Storage.build_path_and_ensure_state_file!(
|
27
|
+
state.strip, @cartridge
|
28
|
+
)
|
29
|
+
@state = load_state
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def debug
|
34
|
+
pp({
|
35
|
+
state: {
|
36
|
+
path: @state_path,
|
37
|
+
content: @state
|
38
|
+
}
|
39
|
+
})
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_state
|
43
|
+
@state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(File.read(@state_path)))
|
44
|
+
end
|
45
|
+
|
46
|
+
def store_state!
|
47
|
+
File.write(@state_path, JSON.generate(@state))
|
48
|
+
end
|
49
|
+
|
50
|
+
def boot(mode:)
|
51
|
+
return unless Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors boot instruction])
|
52
|
+
|
53
|
+
behavior = Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors boot]) || {}
|
54
|
+
|
55
|
+
input = { behavior:, history: [] }
|
56
|
+
|
57
|
+
process(input, mode:)
|
58
|
+
end
|
59
|
+
|
60
|
+
def evaluate_and_print(message, mode:)
|
61
|
+
behavior = Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors interaction]) || {}
|
62
|
+
|
63
|
+
@state[:history] << ({ who: 'user', message: })
|
64
|
+
|
65
|
+
input = { behavior:, history: @state[:history] }
|
66
|
+
|
67
|
+
process(input, mode:)
|
68
|
+
end
|
69
|
+
|
70
|
+
def process(input, mode:)
|
71
|
+
streaming = @provider.settings[:stream] && Logic::Helpers::Hash.fetch(
|
72
|
+
@cartridge, [:interfaces, mode.to_sym, :stream]
|
73
|
+
)
|
74
|
+
|
75
|
+
interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
|
76
|
+
|
77
|
+
input[:interface] = interface
|
78
|
+
|
79
|
+
updated_at = Time.now
|
80
|
+
|
81
|
+
ready = false
|
82
|
+
@provider.evaluate(input) do |output, finished|
|
83
|
+
updated_at = Time.now
|
84
|
+
if finished
|
85
|
+
@state[:history] << output
|
86
|
+
self.print(output[:message]) unless streaming
|
87
|
+
unless Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym, :postfix]).nil?
|
88
|
+
self.print(Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym, :postfix]))
|
89
|
+
end
|
90
|
+
ready = true
|
91
|
+
flush
|
92
|
+
elsif streaming
|
93
|
+
self.print(output[:message])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
until ready
|
98
|
+
seconds = (Time.now - updated_at).to_i
|
99
|
+
raise StandardError, 'The stream has become unresponsive.' if seconds >= STREAM_TIMEOUT_IN_SECONDS
|
100
|
+
end
|
101
|
+
|
102
|
+
store_state! unless @stateless
|
103
|
+
end
|
104
|
+
|
105
|
+
def flush
|
106
|
+
@output.flush
|
107
|
+
end
|
108
|
+
|
109
|
+
def print(content)
|
110
|
+
@output.write(content)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NanoBot
|
4
|
+
module Logic
|
5
|
+
module Helpers
|
6
|
+
module Hash
|
7
|
+
def self.symbolize_keys(object)
|
8
|
+
case object
|
9
|
+
when ::Hash
|
10
|
+
object.each_with_object({}) do |(key, value), result|
|
11
|
+
result[key.to_sym] = symbolize_keys(value)
|
12
|
+
end
|
13
|
+
when Array
|
14
|
+
object.map { |e| symbolize_keys(e) }
|
15
|
+
else
|
16
|
+
object
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.fetch(object, path)
|
21
|
+
node = object
|
22
|
+
|
23
|
+
return nil unless node
|
24
|
+
|
25
|
+
path.each do |key|
|
26
|
+
node = node[key]
|
27
|
+
break if node.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
node
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/nano-bots.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'static/gem'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = NanoBot::GEM[:name]
|
7
|
+
spec.version = NanoBot::GEM[:version]
|
8
|
+
spec.authors = [NanoBot::GEM[:author]]
|
9
|
+
|
10
|
+
spec.summary = NanoBot::GEM[:summary]
|
11
|
+
spec.description = NanoBot::GEM[:description]
|
12
|
+
|
13
|
+
spec.homepage = NanoBot::GEM[:github]
|
14
|
+
|
15
|
+
spec.license = NanoBot::GEM[:license]
|
16
|
+
|
17
|
+
spec.required_ruby_version = Gem::Requirement.new(">= #{NanoBot::GEM[:ruby]}")
|
18
|
+
|
19
|
+
spec.metadata['allowed_push_host'] = NanoBot::GEM[:gem_server]
|
20
|
+
|
21
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
22
|
+
spec.metadata['source_code_uri'] = NanoBot::GEM[:github]
|
23
|
+
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
f.match(%r{\A(?:test|spec|features)/})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
spec.require_paths = ['ports/dsl']
|
31
|
+
|
32
|
+
spec.executables = ['rnb']
|
33
|
+
|
34
|
+
spec.add_dependency 'babosa', '~> 2.0'
|
35
|
+
spec.add_dependency 'dotenv', '~> 2.8', '>= 2.8.1'
|
36
|
+
spec.add_dependency 'faraday', '~> 2.7', '>= 2.7.4'
|
37
|
+
spec.add_dependency 'pry', '~> 0.14.2'
|
38
|
+
spec.add_dependency 'rainbow', '~> 3.1', '>= 3.1.1'
|
39
|
+
spec.add_dependency 'ruby-openai', '~> 4.0'
|
40
|
+
|
41
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dotenv/load'
|
4
|
+
|
5
|
+
require_relative '../../static/gem'
|
6
|
+
require_relative '../../controllers/instance'
|
7
|
+
require_relative '../../controllers/interfaces/cli'
|
8
|
+
|
9
|
+
module NanoBot
|
10
|
+
def self.new(cartridge:, state: '-')
|
11
|
+
Controllers::Instance.new(cartridge_path: cartridge, state:)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.cli
|
15
|
+
Controllers::Interfaces::CLI.handle!
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.repl(cartridge:, state: '-')
|
19
|
+
Controllers::Instance.new(cartridge_path: cartridge, state:).repl
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.version
|
23
|
+
NanoBot::GEM[:version]
|
24
|
+
end
|
25
|
+
end
|
data/static/gem.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NanoBot
|
4
|
+
GEM = {
|
5
|
+
name: 'nano-bots',
|
6
|
+
version: '0.0.1',
|
7
|
+
author: 'icebaker',
|
8
|
+
summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots',
|
9
|
+
description: 'Ruby Implementation of Nano Bots: small, AI-powered bots easily shared as a single file, designed to support multiple providers such as Vicuna, OpenAI ChatGPT, Google PaLM, Alpaca, and LLaMA.',
|
10
|
+
github: 'https://github.com/icebaker/ruby-nano-bots',
|
11
|
+
gem_server: 'https://rubygems.org',
|
12
|
+
license: 'MIT',
|
13
|
+
ruby: '3.1.4'
|
14
|
+
}.freeze
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nano-bots
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- icebaker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: babosa
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dotenv
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.8'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 2.8.1
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.8'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.8.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: faraday
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.7'
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 2.7.4
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '2.7'
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 2.7.4
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: pry
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.14.2
|
74
|
+
type: :runtime
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 0.14.2
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rainbow
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '3.1'
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 3.1.1
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.1'
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 3.1.1
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: ruby-openai
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - "~>"
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '4.0'
|
108
|
+
type: :runtime
|
109
|
+
prerelease: false
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - "~>"
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '4.0'
|
115
|
+
description: 'Ruby Implementation of Nano Bots: small, AI-powered bots easily shared
|
116
|
+
as a single file, designed to support multiple providers such as Vicuna, OpenAI
|
117
|
+
ChatGPT, Google PaLM, Alpaca, and LLaMA.'
|
118
|
+
email:
|
119
|
+
executables:
|
120
|
+
- rnb
|
121
|
+
extensions: []
|
122
|
+
extra_rdoc_files: []
|
123
|
+
files:
|
124
|
+
- ".gitignore"
|
125
|
+
- ".rspec"
|
126
|
+
- ".rubocop.yml"
|
127
|
+
- Gemfile
|
128
|
+
- Gemfile.lock
|
129
|
+
- LICENSE
|
130
|
+
- README.md
|
131
|
+
- bin/rnb
|
132
|
+
- components/provider.rb
|
133
|
+
- components/providers/base.rb
|
134
|
+
- components/providers/openai.rb
|
135
|
+
- components/storage.rb
|
136
|
+
- controllers/instance.rb
|
137
|
+
- controllers/interfaces/cli.rb
|
138
|
+
- controllers/interfaces/repl.rb
|
139
|
+
- controllers/session.rb
|
140
|
+
- logic/helpers/hash.rb
|
141
|
+
- nano-bots.gemspec
|
142
|
+
- ports/dsl/nano-bots.rb
|
143
|
+
- ports/dsl/nano-bots/cli.rb
|
144
|
+
- static/gem.rb
|
145
|
+
homepage: https://github.com/icebaker/ruby-nano-bots
|
146
|
+
licenses:
|
147
|
+
- MIT
|
148
|
+
metadata:
|
149
|
+
allowed_push_host: https://rubygems.org
|
150
|
+
homepage_uri: https://github.com/icebaker/ruby-nano-bots
|
151
|
+
source_code_uri: https://github.com/icebaker/ruby-nano-bots
|
152
|
+
rubygems_mfa_required: 'true'
|
153
|
+
post_install_message:
|
154
|
+
rdoc_options: []
|
155
|
+
require_paths:
|
156
|
+
- ports/dsl
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: 3.1.4
|
162
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
requirements: []
|
168
|
+
rubygems_version: 3.4.10
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots'
|
172
|
+
test_files: []
|