nano-bots 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -3
- data/README.md +139 -19
- data/components/crypto.rb +43 -0
- data/components/provider.rb +4 -4
- data/components/providers/base.rb +4 -0
- data/components/providers/openai.rb +35 -10
- data/components/storage.rb +39 -4
- data/controllers/cartridges.rb +1 -1
- data/controllers/instance.rb +5 -3
- data/controllers/interfaces/cli.rb +23 -0
- data/controllers/interfaces/eval.rb +0 -4
- data/controllers/security.rb +31 -0
- data/controllers/session.rb +8 -4
- data/docker-compose.example.yml +6 -5
- data/nano-bots.gemspec +1 -0
- data/ports/dsl/nano-bots.rb +16 -4
- data/static/cartridges/baseline.yml +9 -6
- data/static/cartridges/default.yml +0 -1
- data/static/fennel/LICENSE +21 -0
- data/static/gem.rb +1 -1
- metadata +25 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4632306b0fe1fc9af4ee96c7624f163d80c70eb259c80f3efad158e95e76414a
|
4
|
+
data.tar.gz: a2156c47648e3b8b118a4698d23031ca4bd4bf139db6f5f19fa5561f1591cf7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8271268413e87793b147385c999edddbb59dc70203dfca243adb7836ee680dc430a57dc625d038394b6b86e3bb0a9a4e45f12291f9e1ef9574da8ee69ed93e5
|
7
|
+
data.tar.gz: 1d6d1118162ed3dbc3438205aa7b4e858499d154e7cc6bdba963a53ddeeff7c071398e9eebc1f3d273c7aa128c39fbcb452a8f6f9bfff8d41b2dac81fb04d9ce
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nano-bots (0.0
|
4
|
+
nano-bots (0.1.0)
|
5
5
|
babosa (~> 2.0)
|
6
6
|
dotenv (~> 2.8, >= 2.8.1)
|
7
7
|
faraday (~> 2.7, >= 2.7.5)
|
8
8
|
pry (~> 0.14.2)
|
9
9
|
rainbow (~> 3.1, >= 3.1.1)
|
10
|
+
rbnacl (~> 7.1, >= 7.1.1)
|
10
11
|
ruby-openai (~> 4.0)
|
11
12
|
sweet-moon (~> 0.0.7)
|
12
13
|
|
@@ -35,6 +36,8 @@ GEM
|
|
35
36
|
coderay (~> 1.1)
|
36
37
|
method_source (~> 1.0)
|
37
38
|
rainbow (3.1.1)
|
39
|
+
rbnacl (7.1.1)
|
40
|
+
ffi
|
38
41
|
regexp_parser (2.8.0)
|
39
42
|
rexml (3.2.5)
|
40
43
|
rspec (3.12.0)
|
@@ -50,7 +53,7 @@ GEM
|
|
50
53
|
diff-lcs (>= 1.2.0, < 2.0)
|
51
54
|
rspec-support (~> 3.12.0)
|
52
55
|
rspec-support (3.12.0)
|
53
|
-
rubocop (1.
|
56
|
+
rubocop (1.52.0)
|
54
57
|
json (~> 2.3)
|
55
58
|
parallel (~> 1.10)
|
56
59
|
parser (>= 3.2.0.0)
|
@@ -60,7 +63,7 @@ GEM
|
|
60
63
|
rubocop-ast (>= 1.28.0, < 2.0)
|
61
64
|
ruby-progressbar (~> 1.7)
|
62
65
|
unicode-display_width (>= 2.4.0, < 3.0)
|
63
|
-
rubocop-ast (1.
|
66
|
+
rubocop-ast (1.29.0)
|
64
67
|
parser (>= 3.2.1.0)
|
65
68
|
rubocop-capybara (2.18.0)
|
66
69
|
rubocop (~> 1.41)
|
data/README.md
CHANGED
@@ -13,6 +13,11 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
|
|
13
13
|
- [Command Line](#command-line)
|
14
14
|
- [Library](#library)
|
15
15
|
- [Cartridges](#cartridges)
|
16
|
+
- [Marketplace](#marketplace)
|
17
|
+
- [Security and Privacy](#security-and-privacy)
|
18
|
+
- [Cryptography](#cryptography)
|
19
|
+
- [End-user IDs](#end-user-ids)
|
20
|
+
- [Decrypting](#decrypting)
|
16
21
|
- [Providers](#providers)
|
17
22
|
- [Debugging](#debugging)
|
18
23
|
- [Development](#development)
|
@@ -23,13 +28,13 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
|
|
23
28
|
For a system usage:
|
24
29
|
|
25
30
|
```sh
|
26
|
-
gem install nano-bots -v 0.0
|
31
|
+
gem install nano-bots -v 0.1.0
|
27
32
|
```
|
28
33
|
|
29
34
|
To use it in a project, add it to your `Gemfile`:
|
30
35
|
|
31
36
|
```ruby
|
32
|
-
gem 'nano-bots', '~> 0.0
|
37
|
+
gem 'nano-bots', '~> 0.1.0'
|
33
38
|
```
|
34
39
|
|
35
40
|
```sh
|
@@ -40,8 +45,10 @@ For credentials and configurations, relevant environment variables can be set in
|
|
40
45
|
|
41
46
|
```sh
|
42
47
|
export OPENAI_API_ADDRESS=https://api.openai.com
|
43
|
-
export
|
44
|
-
|
48
|
+
export OPENAI_API_KEY=your-access-token
|
49
|
+
|
50
|
+
export NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
|
51
|
+
export NANO_BOTS_END_USER=your-user
|
45
52
|
|
46
53
|
# export NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
|
47
54
|
# export NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
|
@@ -51,8 +58,10 @@ Alternatively, if your current directory has a `.env` file with the environment
|
|
51
58
|
|
52
59
|
```sh
|
53
60
|
OPENAI_API_ADDRESS=https://api.openai.com
|
54
|
-
|
55
|
-
|
61
|
+
OPENAI_API_KEY=your-access-token
|
62
|
+
|
63
|
+
NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
|
64
|
+
NANO_BOTS_END_USER=your-user
|
56
65
|
|
57
66
|
# NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
|
58
67
|
# NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
|
@@ -76,14 +85,15 @@ version: '3.7'
|
|
76
85
|
services:
|
77
86
|
nano-bots:
|
78
87
|
image: ruby:3.2.2-slim-bullseye
|
79
|
-
command: sh -c "gem install nano-bots -v 0.0
|
88
|
+
command: sh -c "gem install nano-bots -v 0.1.0 && bash"
|
80
89
|
environment:
|
81
90
|
OPENAI_API_ADDRESS: https://api.openai.com
|
82
|
-
|
83
|
-
|
91
|
+
OPENAI_API_KEY: your-access-token
|
92
|
+
NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
|
93
|
+
NANO_BOTS_END_USER: your-user
|
84
94
|
volumes:
|
85
|
-
- ./your-cartridges
|
86
|
-
|
95
|
+
- ./your-cartridges:/.local/share/nano-bots/cartridges
|
96
|
+
- ./your-state:/.local/state/nano-bots
|
87
97
|
```
|
88
98
|
|
89
99
|
Enter the container:
|
@@ -96,8 +106,8 @@ Start playing:
|
|
96
106
|
nb - - eval "hello"
|
97
107
|
nb - - repl
|
98
108
|
|
99
|
-
nb
|
100
|
-
nb
|
109
|
+
nb assistant.yml - eval "hello"
|
110
|
+
nb assistant.yml - repl
|
101
111
|
```
|
102
112
|
|
103
113
|
## Usage
|
@@ -248,19 +258,129 @@ behaviors:
|
|
248
258
|
directive: You are a helpful assistant.
|
249
259
|
|
250
260
|
provider:
|
251
|
-
|
261
|
+
id: openai
|
262
|
+
credentials:
|
263
|
+
address: ENV/OPENAI_API_ADDRESS
|
264
|
+
access-token: ENV/OPENAI_API_KEY
|
252
265
|
settings:
|
266
|
+
user: ENV/NANO_BOTS_END_USER
|
253
267
|
model: gpt-3.5-turbo
|
254
|
-
credentials:
|
255
|
-
address: ENV/OPENAI_API_ADDRESS
|
256
|
-
access-token: ENV/OPENAI_API_ACCESS_TOKEN
|
257
|
-
user-identifier: ENV/OPENAI_API_USER_IDENTIFIER
|
258
268
|
```
|
259
269
|
|
260
270
|
Check the Nano Bots specification to learn more about [how to build cartridges](https://spec.nbots.io/#/README?id=cartridges).
|
261
271
|
|
262
272
|
Try the [Nano Bots Clinic (Live Editor)](https://clinic.nbots.io) to learn about creating Cartridges.
|
263
273
|
|
274
|
+
### Marketplace
|
275
|
+
|
276
|
+
You can explore the Nano Bots [Marketplace](https://nbots.io) to discover new Cartridges that can help you.
|
277
|
+
|
278
|
+
## Security and Privacy
|
279
|
+
|
280
|
+
Each provider will have its own security and privacy policies (e.g. [OpenAI Policy](https://openai.com/policies/api-data-usage-policies)), so you must consult them to understand their implications.
|
281
|
+
|
282
|
+
### Cryptography
|
283
|
+
|
284
|
+
By default, all states stored in your local disk are encrypted.
|
285
|
+
|
286
|
+
To ensure that the encryption is secure, you need to define a password through the `NANO_BOTS_ENCRYPTION_PASSWORD` environment variable. Otherwise, although the content will be encrypted, anyone would be able to decrypt it without a password.
|
287
|
+
|
288
|
+
It's important to note that the content shared with providers, despite being transmitted over secure connections (e.g., [HTTPS](https://en.wikipedia.org/wiki/HTTPS)), will be readable by the provider. This is because providers need to operate on the data, which would not be possible if the content was encrypted beyond HTTPS. So, the data stored locally on your system is encrypted, which does not mean that what you share with providers will not be readable by them.
|
289
|
+
|
290
|
+
To ensure that your encryption and password are configured properly, you can run the following command:
|
291
|
+
```sh
|
292
|
+
nb security
|
293
|
+
```
|
294
|
+
|
295
|
+
Which should return:
|
296
|
+
```text
|
297
|
+
✅ Encryption is enabled and properly working.
|
298
|
+
This means that your data is stored in an encrypted format on your disk.
|
299
|
+
|
300
|
+
✅ A password is being used for the encrypted content.
|
301
|
+
This means that only those who possess the password can decrypt your data.
|
302
|
+
```
|
303
|
+
|
304
|
+
Alternatively, you can check it at runtime with:
|
305
|
+
```ruby
|
306
|
+
require 'nano-bots'
|
307
|
+
|
308
|
+
NanoBot.security.check
|
309
|
+
# => { encryption: true, password: true }
|
310
|
+
```
|
311
|
+
|
312
|
+
#### End-user IDs
|
313
|
+
|
314
|
+
A common strategy for deploying Nano Bots to multiple users through APIs or automations is to assign a unique [end-user ID](https://platform.openai.com/docs/guides/safety-best-practices/end-user-ids) for each user. This can be useful if any of your users violate the provider's policy due to abusive behavior. By providing the end-user ID, you can unravel that even though the activity originated from your API Key, the actions taken were not your own.
|
315
|
+
|
316
|
+
You can define custom end-user identifiers in the following way:
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
NanoBot.new(environment: { NANO_BOTS_END_USER: 'custom-user-a' })
|
320
|
+
NanoBot.new(environment: { NANO_BOTS_END_USER: 'custom-user-b' })
|
321
|
+
```
|
322
|
+
|
323
|
+
Consider that you have the following end-user identifier in your environment:
|
324
|
+
```sh
|
325
|
+
NANO_BOTS_END_USER=your-name
|
326
|
+
```
|
327
|
+
|
328
|
+
Or a configuration in your Cartridge:
|
329
|
+
```yml
|
330
|
+
---
|
331
|
+
provider:
|
332
|
+
id: openai
|
333
|
+
settings:
|
334
|
+
user: your-name
|
335
|
+
```
|
336
|
+
|
337
|
+
The requests will be performed as follows:
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
NanoBot.new(cartridge: '-')
|
341
|
+
# { user: 'your-name' }
|
342
|
+
|
343
|
+
NanoBot.new(cartridge: '-', environment: { NANO_BOTS_END_USER: 'custom-user-a' })
|
344
|
+
# { user: 'custom-user-a' }
|
345
|
+
|
346
|
+
NanoBot.new(cartridge: '-', environment: { NANO_BOTS_END_USER: 'custom-user-b' })
|
347
|
+
# { user: 'custom-user-b' }
|
348
|
+
```
|
349
|
+
|
350
|
+
Actually, to enhance privacy, neither your user nor your users' identifiers will be shared in this way. Instead, they will be encrypted before being shared with the provider:
|
351
|
+
|
352
|
+
```ruby
|
353
|
+
'your-name'
|
354
|
+
# _O7OjYUESagb46YSeUeSfSMzoO1Yg0BZqpsAkPg4j62SeNYlgwq3kn51Ob2wmIehoA==
|
355
|
+
|
356
|
+
'custom-user-a'
|
357
|
+
# _O7OjYUESagb46YSeUeSfSMzoO1Yg0BZJgIXHCBHyADW-rn4IQr-s2RvP7vym8u5tnzYMIs=
|
358
|
+
|
359
|
+
'custom-user-b'
|
360
|
+
# _O7OjYUESagb46YSeUeSfSMzoO1Yg0BZkjUwCcsh9sVppKvYMhd2qGRvP7vym8u5tnzYMIg=
|
361
|
+
```
|
362
|
+
|
363
|
+
In this manner, you possess identifiers if required, however, their actual content can only be decrypted by you via your secure password (`NANO_BOTS_ENCRYPTION_PASSWORD`).
|
364
|
+
|
365
|
+
## Decrypting
|
366
|
+
|
367
|
+
To decrypt your encrypted data, once you have properly configured your password, you can simply run:
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
require 'nano-bots'
|
371
|
+
|
372
|
+
NanoBot.security.decrypt('_O7OjYUESagb46YSeUeSfSMzoO1Yg0BZqpsAkPg4j62SeNYlgwq3kn51Ob2wmIehoA==')
|
373
|
+
# your-name
|
374
|
+
|
375
|
+
NanoBot.security.decrypt('_O7OjYUESagb46YSeUeSfSMzoO1Yg0BZJgIXHCBHyADW-rn4IQr-s2RvP7vym8u5tnzYMIs=')
|
376
|
+
# custom-user-a
|
377
|
+
|
378
|
+
NanoBot.security.decrypt('_O7OjYUESagb46YSeUeSfSMzoO1Yg0BZkjUwCcsh9sVppKvYMhd2qGRvP7vym8u5tnzYMIg=')
|
379
|
+
# custom-user-b
|
380
|
+
```
|
381
|
+
|
382
|
+
If you lose your password, you lose your data. It is not possible to recover it at all. For real.
|
383
|
+
|
264
384
|
## Providers
|
265
385
|
|
266
386
|
Currently supported providers:
|
@@ -288,5 +408,5 @@ gem build nano-bots.gemspec
|
|
288
408
|
|
289
409
|
gem signin
|
290
410
|
|
291
|
-
gem push nano-bots-0.0.
|
411
|
+
gem push nano-bots-0.1.0.gem
|
292
412
|
```
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require 'rbnacl'
|
5
|
+
require 'base64'
|
6
|
+
|
7
|
+
module NanoBot
|
8
|
+
module Components
|
9
|
+
class Crypto
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
password = ENV.fetch('NANO_BOTS_ENCRYPTION_PASSWORD', nil)
|
14
|
+
|
15
|
+
password = 'UNSAFE' unless password && password != ''
|
16
|
+
|
17
|
+
@box = RbNaCl::SecretBox.new(RbNaCl::Hash.sha256(password))
|
18
|
+
@fixed_nonce = RbNaCl::Hash.sha256(password)[0...@box.nonce_bytes]
|
19
|
+
end
|
20
|
+
|
21
|
+
def encrypt(content, soft: false)
|
22
|
+
nonce = soft ? @fixed_nonce : RbNaCl::Random.random_bytes(@box.nonce_bytes)
|
23
|
+
Base64.urlsafe_encode64(nonce + @box.encrypt(nonce, content))
|
24
|
+
end
|
25
|
+
|
26
|
+
def decrypt(content)
|
27
|
+
decoded_content = Base64.urlsafe_decode64(content)
|
28
|
+
nonce = decoded_content[0...@box.nonce_bytes]
|
29
|
+
cipher_text = decoded_content[@box.nonce_bytes..]
|
30
|
+
|
31
|
+
@box.decrypt(nonce, cipher_text)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.encrypt(content, soft: false)
|
35
|
+
instance.encrypt(content, soft:)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.decrypt(content)
|
39
|
+
instance.decrypt(content)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/components/provider.rb
CHANGED
@@ -7,12 +7,12 @@ require_relative './providers/openai'
|
|
7
7
|
module NanoBot
|
8
8
|
module Components
|
9
9
|
class Provider
|
10
|
-
def self.new(provider)
|
11
|
-
case provider[:
|
10
|
+
def self.new(provider, environment: {})
|
11
|
+
case provider[:id]
|
12
12
|
when 'openai'
|
13
|
-
Providers::OpenAI.new(provider[:settings])
|
13
|
+
Providers::OpenAI.new(provider[:settings], provider[:credentials], environment:)
|
14
14
|
else
|
15
|
-
raise "Unsupported provider #{provider[:
|
15
|
+
raise "Unsupported provider \"#{provider[:id]}\""
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -6,6 +6,10 @@ module NanoBot
|
|
6
6
|
module Components
|
7
7
|
module Providers
|
8
8
|
class Base
|
9
|
+
def initialize(_settings, _credentials, _environment: {})
|
10
|
+
raise NoMethodError, "The 'initialize' method is not implemented for the current provider."
|
11
|
+
end
|
12
|
+
|
9
13
|
def evaluate(_payload)
|
10
14
|
raise NoMethodError, "The 'evaluate' method is not implemented for the current provider."
|
11
15
|
end
|
@@ -3,11 +3,14 @@
|
|
3
3
|
require 'openai'
|
4
4
|
|
5
5
|
require_relative './base'
|
6
|
+
require_relative '../crypto'
|
6
7
|
|
7
8
|
module NanoBot
|
8
9
|
module Components
|
9
10
|
module Providers
|
10
11
|
class OpenAI < Base
|
12
|
+
DEFAULT_ADDRESS = 'https://api.openai.com'
|
13
|
+
|
11
14
|
CHAT_SETTINGS = %i[
|
12
15
|
model stream temperature top_p n stop max_tokens
|
13
16
|
presence_penalty frequency_penalty logit_bias
|
@@ -15,13 +18,18 @@ module NanoBot
|
|
15
18
|
|
16
19
|
attr_reader :settings
|
17
20
|
|
18
|
-
def initialize(settings)
|
21
|
+
def initialize(settings, credentials, environment: {})
|
19
22
|
@settings = settings
|
23
|
+
@credentials = credentials
|
24
|
+
@environment = environment
|
20
25
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
26
|
+
uri_base = if @credentials[:address].nil? || @credentials[:address].to_s.strip.empty?
|
27
|
+
"#{DEFAULT_ADDRESS}/"
|
28
|
+
else
|
29
|
+
"#{@credentials[:address].to_s.sub(%r{/$}, '')}/"
|
30
|
+
end
|
31
|
+
|
32
|
+
@client = ::OpenAI::Client.new(uri_base:, access_token: @credentials[:'access-token'])
|
25
33
|
end
|
26
34
|
|
27
35
|
def stream(input)
|
@@ -46,11 +54,7 @@ module NanoBot
|
|
46
54
|
)
|
47
55
|
end
|
48
56
|
|
49
|
-
payload = {
|
50
|
-
model: @settings[:model],
|
51
|
-
user: @settings[:credentials][:'user-identifier'],
|
52
|
-
messages:
|
53
|
-
}
|
57
|
+
payload = { user: OpenAI.end_user(@settings, @environment), messages: }
|
54
58
|
|
55
59
|
CHAT_SETTINGS.each do |key|
|
56
60
|
payload[key] = @settings[key] if @settings.key?(key)
|
@@ -80,6 +84,27 @@ module NanoBot
|
|
80
84
|
block.call({ who: 'AI', message: result.dig('choices', 0, 'message', 'content') }, true)
|
81
85
|
end
|
82
86
|
end
|
87
|
+
|
88
|
+
def self.end_user(settings, environment)
|
89
|
+
user = ENV.fetch('NANO_BOTS_END_USER', nil)
|
90
|
+
|
91
|
+
user = settings[:user] if !settings[:user].nil? && !settings[:user].to_s.strip.empty?
|
92
|
+
|
93
|
+
candidate = environment && (
|
94
|
+
environment['NANO_BOTS_END_USER'] ||
|
95
|
+
environment[:NANO_BOTS_END_USER]
|
96
|
+
)
|
97
|
+
|
98
|
+
user = candidate if !candidate.nil? && !candidate.to_s.strip.empty?
|
99
|
+
|
100
|
+
user = if user.nil? || user.to_s.strip.empty?
|
101
|
+
'unknown'
|
102
|
+
else
|
103
|
+
user.to_s.strip
|
104
|
+
end
|
105
|
+
|
106
|
+
Crypto.encrypt(user, soft: true)
|
107
|
+
end
|
83
108
|
end
|
84
109
|
end
|
85
110
|
end
|
data/components/storage.rb
CHANGED
@@ -3,11 +3,37 @@
|
|
3
3
|
require 'babosa'
|
4
4
|
|
5
5
|
require_relative '../logic/helpers/hash'
|
6
|
+
require_relative './crypto'
|
6
7
|
|
7
8
|
module NanoBot
|
8
9
|
module Components
|
9
10
|
class Storage
|
10
|
-
def self.
|
11
|
+
def self.end_user(cartridge, environment)
|
12
|
+
user = ENV.fetch('NANO_BOTS_END_USER', nil)
|
13
|
+
|
14
|
+
if cartridge[:provider][:id] == 'openai' &&
|
15
|
+
!cartridge[:provider][:settings][:user].nil? &&
|
16
|
+
!cartridge[:provider][:settings][:user].to_s.strip.empty?
|
17
|
+
user = cartridge[:provider][:settings][:user]
|
18
|
+
end
|
19
|
+
|
20
|
+
candidate = environment && (
|
21
|
+
environment['NANO_BOTS_END_USER'] ||
|
22
|
+
environment[:NANO_BOTS_END_USER]
|
23
|
+
)
|
24
|
+
|
25
|
+
user = candidate if !candidate.nil? && !candidate.to_s.strip.empty?
|
26
|
+
|
27
|
+
user = if user.nil? || user.to_s.strip.empty?
|
28
|
+
'unknown'
|
29
|
+
else
|
30
|
+
user.to_s.strip
|
31
|
+
end
|
32
|
+
|
33
|
+
Crypto.encrypt(user, soft: true)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.build_path_and_ensure_state_file!(key, cartridge, environment: {})
|
11
37
|
path = [
|
12
38
|
Logic::Helpers::Hash.fetch(cartridge, %i[state directory]),
|
13
39
|
ENV.fetch('NANO_BOTS_STATE_DIRECTORY', nil)
|
@@ -17,14 +43,23 @@ module NanoBot
|
|
17
43
|
|
18
44
|
path = "#{user_home!.sub(%r{/$}, '')}/.local/state/nano-bots" if path.nil?
|
19
45
|
|
20
|
-
path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots
|
46
|
+
path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots"
|
47
|
+
|
48
|
+
path = "#{path}/#{cartridge[:meta][:author].to_slug.normalize}"
|
21
49
|
path = "#{path}/#{cartridge[:meta][:name].to_slug.normalize}"
|
22
|
-
path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}
|
50
|
+
path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}"
|
51
|
+
path = "#{path}/#{end_user(cartridge, environment)}"
|
52
|
+
path = "#{path}/#{Crypto.encrypt(key, soft: true)}"
|
23
53
|
path = "#{path}/state.json"
|
24
54
|
|
25
55
|
FileUtils.mkdir_p(File.dirname(path))
|
26
56
|
|
27
|
-
|
57
|
+
unless File.exist?(path)
|
58
|
+
File.write(
|
59
|
+
path,
|
60
|
+
Crypto.encrypt(JSON.generate({ key:, history: [] }))
|
61
|
+
)
|
62
|
+
end
|
28
63
|
|
29
64
|
path
|
30
65
|
end
|
data/controllers/cartridges.rb
CHANGED
@@ -39,7 +39,7 @@ module NanoBot
|
|
39
39
|
rescue StandardError => _e
|
40
40
|
end
|
41
41
|
|
42
|
-
cartridges.sort_by { |cartridge| cartridge[:meta][:name] }
|
42
|
+
cartridges = cartridges.sort_by { |cartridge| cartridge[:meta][:name] }
|
43
43
|
|
44
44
|
cartridges.prepend(
|
45
45
|
{ system: { id: '-' }, meta: { name: 'Default', symbol: '🤖' } }
|
data/controllers/instance.rb
CHANGED
@@ -13,14 +13,16 @@ require_relative './session'
|
|
13
13
|
module NanoBot
|
14
14
|
module Controllers
|
15
15
|
class Instance
|
16
|
-
def initialize(cartridge_path:, stream:, state: nil)
|
16
|
+
def initialize(cartridge_path:, stream:, state: nil, environment: {})
|
17
17
|
@stream = stream
|
18
18
|
|
19
19
|
load_cartridge!(cartridge_path)
|
20
20
|
|
21
|
-
provider = Components::Provider.new(@cartridge[:provider])
|
21
|
+
provider = Components::Provider.new(@cartridge[:provider], environment:)
|
22
22
|
|
23
|
-
@session = Session.new(
|
23
|
+
@session = Session.new(
|
24
|
+
provider:, cartridge: @cartridge, state:, stream: @stream, environment:
|
25
|
+
)
|
24
26
|
end
|
25
27
|
|
26
28
|
def cartridge
|
@@ -11,6 +11,28 @@ module NanoBot
|
|
11
11
|
case ARGV[0]
|
12
12
|
when 'version'
|
13
13
|
puts NanoBot::GEM[:version]
|
14
|
+
exit
|
15
|
+
when 'security'
|
16
|
+
result = NanoBot.security.check
|
17
|
+
|
18
|
+
if result[:encryption]
|
19
|
+
puts "\n✅ Encryption is enabled and properly working."
|
20
|
+
puts ' This means that your data is stored in an encrypted format on your disk.'
|
21
|
+
else
|
22
|
+
puts "\n❌ Encryption is not being utilized to store your content."
|
23
|
+
puts ' This means that your data can be easily read because it is stored in plaintext.'
|
24
|
+
end
|
25
|
+
|
26
|
+
if result[:password]
|
27
|
+
puts "\n✅ A password is being used for the encrypted content."
|
28
|
+
puts ' This means that only those who possess the password can decrypt your data.'
|
29
|
+
else
|
30
|
+
puts "\n❌ No custom password is being used for the encrypted content."
|
31
|
+
puts ' This means that anyone can easily decrypt your data.'
|
32
|
+
end
|
33
|
+
|
34
|
+
puts ''
|
35
|
+
|
14
36
|
exit
|
15
37
|
when 'help', '', nil
|
16
38
|
puts ''
|
@@ -34,6 +56,7 @@ module NanoBot
|
|
34
56
|
puts ' nb - STATE-KEY state'
|
35
57
|
puts ' nb cartridge.yml STATE-KEY state'
|
36
58
|
puts ''
|
59
|
+
puts ' nb security'
|
37
60
|
puts ' nb version'
|
38
61
|
puts ' nb help'
|
39
62
|
puts ''
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../components/crypto'
|
4
|
+
|
5
|
+
module NanoBot
|
6
|
+
module Controllers
|
7
|
+
module Security
|
8
|
+
def self.decrypt(content)
|
9
|
+
Components::Crypto.decrypt(content)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.encrypt(content, soft: false)
|
13
|
+
Components::Crypto.encrypt(content, soft:)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.check
|
17
|
+
password = ENV.fetch('NANO_BOTS_ENCRYPTION_PASSWORD', nil)
|
18
|
+
password = 'UNSAFE' unless password && password != ''
|
19
|
+
|
20
|
+
{
|
21
|
+
encryption: (
|
22
|
+
Components::Crypto.encrypt('SAFE') != 'SAFE' &&
|
23
|
+
Components::Crypto.encrypt('SAFE') != Components::Crypto.encrypt('SAFE') &&
|
24
|
+
Components::Crypto.decrypt(Components::Crypto.encrypt('SAFE')) == 'SAFE'
|
25
|
+
),
|
26
|
+
password: password != 'UNSAFE'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/controllers/session.rb
CHANGED
@@ -9,6 +9,7 @@ require_relative '../logic/cartridge/streaming'
|
|
9
9
|
require_relative '../logic/cartridge/interaction'
|
10
10
|
require_relative '../components/storage'
|
11
11
|
require_relative '../components/adapter'
|
12
|
+
require_relative '../components/crypto'
|
12
13
|
|
13
14
|
module NanoBot
|
14
15
|
module Controllers
|
@@ -17,7 +18,7 @@ module NanoBot
|
|
17
18
|
class Session
|
18
19
|
attr_accessor :stream
|
19
20
|
|
20
|
-
def initialize(provider:, cartridge:, state: nil, stream: $stdout)
|
21
|
+
def initialize(provider:, cartridge:, state: nil, stream: $stdout, environment: {})
|
21
22
|
@stream = stream
|
22
23
|
@provider = provider
|
23
24
|
@cartridge = cartridge
|
@@ -28,8 +29,9 @@ module NanoBot
|
|
28
29
|
@state = { history: [] }
|
29
30
|
else
|
30
31
|
@state_path = Components::Storage.build_path_and_ensure_state_file!(
|
31
|
-
state.strip, @cartridge
|
32
|
+
state.strip, @cartridge, environment:
|
32
33
|
)
|
34
|
+
|
33
35
|
@state = load_state
|
34
36
|
end
|
35
37
|
end
|
@@ -39,11 +41,13 @@ module NanoBot
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def load_state
|
42
|
-
@state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(
|
44
|
+
@state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(
|
45
|
+
Components::Crypto.decrypt(File.read(@state_path))
|
46
|
+
))
|
43
47
|
end
|
44
48
|
|
45
49
|
def store_state!
|
46
|
-
File.write(@state_path, JSON.generate(@state))
|
50
|
+
File.write(@state_path, Components::Crypto.encrypt(JSON.generate(@state)))
|
47
51
|
end
|
48
52
|
|
49
53
|
def boot(mode:)
|
data/docker-compose.example.yml
CHANGED
@@ -3,11 +3,12 @@ version: '3.7'
|
|
3
3
|
services:
|
4
4
|
nano-bots:
|
5
5
|
image: ruby:3.2.2-slim-bullseye
|
6
|
-
command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev lua5.4-dev && gem install nano-bots -v 0.0
|
6
|
+
command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev && gem install nano-bots -v 0.1.0 && bash"
|
7
7
|
environment:
|
8
8
|
OPENAI_API_ADDRESS: https://api.openai.com
|
9
|
-
|
10
|
-
|
9
|
+
OPENAI_API_KEY: your-access-token
|
10
|
+
NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
|
11
|
+
NANO_BOTS_END_USER: your-user
|
11
12
|
volumes:
|
12
|
-
- ./your-cartridges
|
13
|
-
|
13
|
+
- ./your-cartridges:/.local/share/nano-bots/cartridges
|
14
|
+
- ./your-state:/.local/state/nano-bots
|
data/nano-bots.gemspec
CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency 'faraday', '~> 2.7', '>= 2.7.5'
|
37
37
|
spec.add_dependency 'pry', '~> 0.14.2'
|
38
38
|
spec.add_dependency 'rainbow', '~> 3.1', '>= 3.1.1'
|
39
|
+
spec.add_dependency 'rbnacl', '~> 7.1', '>= 7.1.1'
|
39
40
|
spec.add_dependency 'ruby-openai', '~> 4.0'
|
40
41
|
spec.add_dependency 'sweet-moon', '~> 0.0.7'
|
41
42
|
|
data/ports/dsl/nano-bots.rb
CHANGED
@@ -5,12 +5,22 @@ require 'dotenv/load'
|
|
5
5
|
require_relative '../../static/gem'
|
6
6
|
require_relative '../../controllers/cartridges'
|
7
7
|
require_relative '../../controllers/instance'
|
8
|
+
require_relative '../../controllers/security'
|
8
9
|
require_relative '../../controllers/interfaces/cli'
|
9
10
|
require_relative '../../components/stream'
|
10
11
|
|
11
12
|
module NanoBot
|
12
|
-
def self.new(cartridge: '-', state: '-')
|
13
|
-
Controllers::Instance.new(
|
13
|
+
def self.new(cartridge: '-', state: '-', environment: {})
|
14
|
+
Controllers::Instance.new(
|
15
|
+
cartridge_path: cartridge,
|
16
|
+
state:,
|
17
|
+
stream: Components::Stream.new,
|
18
|
+
environment:
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.security
|
23
|
+
Controllers::Security
|
14
24
|
end
|
15
25
|
|
16
26
|
def self.cartridges
|
@@ -21,8 +31,10 @@ module NanoBot
|
|
21
31
|
Controllers::Interfaces::CLI.handle!
|
22
32
|
end
|
23
33
|
|
24
|
-
def self.repl(cartridge: '-', state: '-')
|
25
|
-
Controllers::Instance.new(
|
34
|
+
def self.repl(cartridge: '-', state: '-', environment: {})
|
35
|
+
Controllers::Instance.new(
|
36
|
+
cartridge_path: cartridge, state:, stream: $stdout, environment:
|
37
|
+
).repl
|
26
38
|
end
|
27
39
|
|
28
40
|
def self.version
|
@@ -1,14 +1,17 @@
|
|
1
1
|
---
|
2
2
|
meta:
|
3
|
+
symbol: 🤖
|
3
4
|
name: Unknown
|
4
|
-
author:
|
5
|
+
author: None
|
5
6
|
version: 0.0.0
|
7
|
+
license: CC0-1.0
|
8
|
+
description: Unknown
|
6
9
|
|
7
10
|
provider:
|
8
|
-
|
11
|
+
id: openai
|
12
|
+
credentials:
|
13
|
+
address: ENV/OPENAI_API_ADDRESS
|
14
|
+
access-token: ENV/OPENAI_API_KEY
|
9
15
|
settings:
|
16
|
+
user: ENV/NANO_BOTS_END_USER
|
10
17
|
model: gpt-3.5-turbo
|
11
|
-
credentials:
|
12
|
-
address: ENV/OPENAI_API_ADDRESS
|
13
|
-
access-token: ENV/OPENAI_API_ACCESS_TOKEN
|
14
|
-
user-identifier: ENV/OPENAI_API_USER_IDENTIFIER
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright © 2016-2022 Calvin Rose and contributors
|
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/static/gem.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module NanoBot
|
4
4
|
GEM = {
|
5
5
|
name: 'nano-bots',
|
6
|
-
version: '0.0
|
6
|
+
version: '0.1.0',
|
7
7
|
author: 'icebaker',
|
8
8
|
summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots',
|
9
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.',
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nano-bots
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icebaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: babosa
|
@@ -98,6 +98,26 @@ dependencies:
|
|
98
98
|
- - ">="
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: 3.1.1
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: rbnacl
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - "~>"
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '7.1'
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 7.1.1
|
111
|
+
type: :runtime
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '7.1'
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 7.1.1
|
101
121
|
- !ruby/object:Gem::Dependency
|
102
122
|
name: ruby-openai
|
103
123
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,6 +164,7 @@ files:
|
|
144
164
|
- README.md
|
145
165
|
- bin/nb
|
146
166
|
- components/adapter.rb
|
167
|
+
- components/crypto.rb
|
147
168
|
- components/provider.rb
|
148
169
|
- components/providers/base.rb
|
149
170
|
- components/providers/openai.rb
|
@@ -154,6 +175,7 @@ files:
|
|
154
175
|
- controllers/interfaces/cli.rb
|
155
176
|
- controllers/interfaces/eval.rb
|
156
177
|
- controllers/interfaces/repl.rb
|
178
|
+
- controllers/security.rb
|
157
179
|
- controllers/session.rb
|
158
180
|
- docker-compose.example.yml
|
159
181
|
- logic/cartridge/adapters.rb
|
@@ -167,6 +189,7 @@ files:
|
|
167
189
|
- ports/dsl/nano-bots/cli.rb
|
168
190
|
- static/cartridges/baseline.yml
|
169
191
|
- static/cartridges/default.yml
|
192
|
+
- static/fennel/LICENSE
|
170
193
|
- static/fennel/fennel.lua
|
171
194
|
- static/gem.rb
|
172
195
|
homepage: https://github.com/icebaker/ruby-nano-bots
|