nano-bots 0.1.1 → 1.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 +4 -4
- data/.ruby-version +1 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +38 -23
- data/README.md +90 -10
- data/components/adapter.rb +16 -30
- data/components/embedding.rb +89 -0
- data/components/provider.rb +1 -1
- data/components/providers/openai/tools.rb +101 -0
- data/components/providers/openai.rb +103 -20
- data/components/storage.rb +1 -1
- data/components/stream.rb +6 -1
- data/controllers/cartridges.rb +1 -1
- data/controllers/instance.rb +4 -4
- data/controllers/interfaces/cli.rb +1 -1
- data/controllers/interfaces/tools.rb +104 -0
- data/controllers/security.rb +3 -4
- data/controllers/session.rb +80 -26
- data/docker-compose.example.yml +3 -4
- data/logic/cartridge/adapters.rb +1 -1
- data/logic/cartridge/affixes.rb +1 -1
- data/logic/cartridge/default.rb +2 -2
- data/logic/cartridge/fetch.rb +24 -0
- data/logic/cartridge/interaction.rb +7 -7
- data/logic/cartridge/safety.rb +39 -0
- data/logic/cartridge/tools.rb +53 -0
- data/logic/providers/openai/tools.rb +58 -0
- data/logic/providers/openai.rb +57 -0
- data/nano-bots.gemspec +3 -2
- data/static/cartridges/baseline.yml +1 -1
- data/static/cartridges/default.yml +16 -0
- data/static/fennel/LICENSE +0 -2
- data/static/fennel/fennel.lua +1763 -1871
- data/static/gem.rb +1 -1
- metadata +36 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c992bede26be258d71c430d42cde2bf369f0ca90b3b835998829008612c7e1aa
|
4
|
+
data.tar.gz: 122564e6d76bab01b3f18e825c75209ca4a6c531957a5f7ed94e8921e40a3d3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55e3ee927659469db0c0533c55241e752ebee17869fe290f9f242de28e3503f74ba7e5c69530d886ce8614b7c68e079c1bf912264c717cb61061e3bfc445e63e
|
7
|
+
data.tar.gz: 8e990faf709f6c720f9ba8f9aab59196e729416f762968f6a02badfe7879b9f24de5ff833b102e99d6aed53a694710f6712b1995e336403c40203dbbff621e1b
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.4
|
data/Gemfile
CHANGED
@@ -5,7 +5,8 @@ source 'https://rubygems.org'
|
|
5
5
|
gemspec
|
6
6
|
|
7
7
|
group :test, :development do
|
8
|
+
gem 'pry-byebug', '~> 3.10', '>= 3.10.1'
|
8
9
|
gem 'rspec', '~> 3.12'
|
9
|
-
gem 'rubocop', '~> 1.
|
10
|
-
gem 'rubocop-rspec', '~> 2.
|
10
|
+
gem 'rubocop', '~> 1.58'
|
11
|
+
gem 'rubocop-rspec', '~> 2.25'
|
11
12
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nano-bots (0.1
|
4
|
+
nano-bots (1.0.1)
|
5
5
|
babosa (~> 2.0)
|
6
|
+
concurrent-ruby (~> 1.2, >= 1.2.2)
|
6
7
|
dotenv (~> 2.8, >= 2.8.1)
|
7
|
-
faraday (~> 2.7, >= 2.7.
|
8
|
+
faraday (~> 2.7, >= 2.7.12)
|
8
9
|
pry (~> 0.14.2)
|
9
10
|
rainbow (~> 3.1, >= 3.1.1)
|
10
11
|
rbnacl (~> 7.1, >= 7.1.1)
|
11
|
-
ruby-openai (~>
|
12
|
+
ruby-openai (~> 6.3)
|
12
13
|
sweet-moon (~> 0.0.7)
|
13
14
|
|
14
15
|
GEM
|
@@ -16,30 +17,41 @@ GEM
|
|
16
17
|
specs:
|
17
18
|
ast (2.4.2)
|
18
19
|
babosa (2.0.0)
|
20
|
+
base64 (0.2.0)
|
21
|
+
byebug (11.1.3)
|
19
22
|
coderay (1.1.3)
|
23
|
+
concurrent-ruby (1.2.2)
|
20
24
|
diff-lcs (1.5.0)
|
21
25
|
dotenv (2.8.1)
|
22
|
-
|
26
|
+
event_stream_parser (1.0.0)
|
27
|
+
faraday (2.7.12)
|
28
|
+
base64
|
23
29
|
faraday-net_http (>= 2.0, < 3.1)
|
24
30
|
ruby2_keywords (>= 0.0.4)
|
25
31
|
faraday-multipart (1.0.4)
|
26
32
|
multipart-post (~> 2)
|
27
33
|
faraday-net_http (3.0.2)
|
28
|
-
ffi (1.
|
29
|
-
json (2.
|
34
|
+
ffi (1.16.3)
|
35
|
+
json (2.7.0)
|
36
|
+
language_server-protocol (3.17.0.3)
|
30
37
|
method_source (1.0.0)
|
31
38
|
multipart-post (2.3.0)
|
32
39
|
parallel (1.23.0)
|
33
|
-
parser (3.2.2.
|
40
|
+
parser (3.2.2.4)
|
34
41
|
ast (~> 2.4.1)
|
42
|
+
racc
|
35
43
|
pry (0.14.2)
|
36
44
|
coderay (~> 1.1)
|
37
45
|
method_source (~> 1.0)
|
46
|
+
pry-byebug (3.10.1)
|
47
|
+
byebug (~> 11.0)
|
48
|
+
pry (>= 0.13, < 0.15)
|
49
|
+
racc (1.7.3)
|
38
50
|
rainbow (3.1.1)
|
39
51
|
rbnacl (7.1.1)
|
40
52
|
ffi
|
41
|
-
regexp_parser (2.8.
|
42
|
-
rexml (3.2.
|
53
|
+
regexp_parser (2.8.2)
|
54
|
+
rexml (3.2.6)
|
43
55
|
rspec (3.12.0)
|
44
56
|
rspec-core (~> 3.12.0)
|
45
57
|
rspec-expectations (~> 3.12.0)
|
@@ -49,47 +61,50 @@ GEM
|
|
49
61
|
rspec-expectations (3.12.3)
|
50
62
|
diff-lcs (>= 1.2.0, < 2.0)
|
51
63
|
rspec-support (~> 3.12.0)
|
52
|
-
rspec-mocks (3.12.
|
64
|
+
rspec-mocks (3.12.6)
|
53
65
|
diff-lcs (>= 1.2.0, < 2.0)
|
54
66
|
rspec-support (~> 3.12.0)
|
55
|
-
rspec-support (3.12.
|
56
|
-
rubocop (1.
|
67
|
+
rspec-support (3.12.1)
|
68
|
+
rubocop (1.58.0)
|
57
69
|
json (~> 2.3)
|
70
|
+
language_server-protocol (>= 3.17.0)
|
58
71
|
parallel (~> 1.10)
|
59
|
-
parser (>= 3.2.
|
72
|
+
parser (>= 3.2.2.4)
|
60
73
|
rainbow (>= 2.2.2, < 4.0)
|
61
74
|
regexp_parser (>= 1.8, < 3.0)
|
62
75
|
rexml (>= 3.2.5, < 4.0)
|
63
|
-
rubocop-ast (>= 1.
|
76
|
+
rubocop-ast (>= 1.30.0, < 2.0)
|
64
77
|
ruby-progressbar (~> 1.7)
|
65
78
|
unicode-display_width (>= 2.4.0, < 3.0)
|
66
|
-
rubocop-ast (1.
|
79
|
+
rubocop-ast (1.30.0)
|
67
80
|
parser (>= 3.2.1.0)
|
68
|
-
rubocop-capybara (2.
|
81
|
+
rubocop-capybara (2.19.0)
|
69
82
|
rubocop (~> 1.41)
|
70
|
-
rubocop-factory_bot (2.
|
71
|
-
rubocop (~> 1.33)
|
72
|
-
rubocop-rspec (2.22.0)
|
83
|
+
rubocop-factory_bot (2.24.0)
|
73
84
|
rubocop (~> 1.33)
|
85
|
+
rubocop-rspec (2.25.0)
|
86
|
+
rubocop (~> 1.40)
|
74
87
|
rubocop-capybara (~> 2.17)
|
75
88
|
rubocop-factory_bot (~> 2.22)
|
76
|
-
ruby-openai (
|
89
|
+
ruby-openai (6.3.0)
|
90
|
+
event_stream_parser (>= 0.3.0, < 2.0.0)
|
77
91
|
faraday (>= 1)
|
78
92
|
faraday-multipart (>= 1)
|
79
93
|
ruby-progressbar (1.13.0)
|
80
94
|
ruby2_keywords (0.0.5)
|
81
95
|
sweet-moon (0.0.7)
|
82
96
|
ffi (~> 1.15, >= 1.15.5)
|
83
|
-
unicode-display_width (2.
|
97
|
+
unicode-display_width (2.5.0)
|
84
98
|
|
85
99
|
PLATFORMS
|
86
100
|
x86_64-linux
|
87
101
|
|
88
102
|
DEPENDENCIES
|
89
103
|
nano-bots!
|
104
|
+
pry-byebug (~> 3.10, >= 3.10.1)
|
90
105
|
rspec (~> 3.12)
|
91
|
-
rubocop (~> 1.
|
92
|
-
rubocop-rspec (~> 2.
|
106
|
+
rubocop (~> 1.58)
|
107
|
+
rubocop-rspec (~> 2.25)
|
93
108
|
|
94
109
|
BUNDLED WITH
|
95
110
|
2.4.13
|
data/README.md
CHANGED
@@ -13,6 +13,8 @@ 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
|
+
- [Tools (Functions)](#tools-functions)
|
17
|
+
- [Experimental Clojure Support](#experimental-clojure-support)
|
16
18
|
- [Marketplace](#marketplace)
|
17
19
|
- [Security and Privacy](#security-and-privacy)
|
18
20
|
- [Cryptography](#cryptography)
|
@@ -28,13 +30,13 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
|
|
28
30
|
For a system usage:
|
29
31
|
|
30
32
|
```sh
|
31
|
-
gem install nano-bots -v 0.1
|
33
|
+
gem install nano-bots -v 1.0.1
|
32
34
|
```
|
33
35
|
|
34
36
|
To use it in a project, add it to your `Gemfile`:
|
35
37
|
|
36
38
|
```ruby
|
37
|
-
gem 'nano-bots', '~> 0.1
|
39
|
+
gem 'nano-bots', '~> 1.0.1'
|
38
40
|
```
|
39
41
|
|
40
42
|
```sh
|
@@ -80,12 +82,11 @@ cp docker-compose.example.yml docker-compose.yml
|
|
80
82
|
Set your provider credentials and choose your desired directory for the cartridges files:
|
81
83
|
|
82
84
|
```yaml
|
83
|
-
|
84
|
-
|
85
|
+
---
|
85
86
|
services:
|
86
87
|
nano-bots:
|
87
|
-
image: ruby:3.2.2-slim-
|
88
|
-
command: sh -c "gem install nano-bots -v 0.1
|
88
|
+
image: ruby:3.2.2-slim-bookworm
|
89
|
+
command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 1.0.1 && bash"
|
89
90
|
environment:
|
90
91
|
OPENAI_API_ADDRESS: https://api.openai.com
|
91
92
|
OPENAI_API_KEY: your-access-token
|
@@ -249,7 +250,7 @@ meta:
|
|
249
250
|
symbol: 🤖
|
250
251
|
name: Nano Bot Name
|
251
252
|
author: Your Name
|
252
|
-
version: 1.0.
|
253
|
+
version: 1.0.1
|
253
254
|
license: CC0-1.0
|
254
255
|
description: A helpful assistant.
|
255
256
|
|
@@ -271,6 +272,85 @@ Check the Nano Bots specification to learn more about [how to build cartridges](
|
|
271
272
|
|
272
273
|
Try the [Nano Bots Clinic (Live Editor)](https://clinic.nbots.io) to learn about creating Cartridges.
|
273
274
|
|
275
|
+
### Tools (Functions)
|
276
|
+
|
277
|
+
Nano Bots can also be powered by _Tools_ (Functions):
|
278
|
+
|
279
|
+
```yaml
|
280
|
+
---
|
281
|
+
tools:
|
282
|
+
- name: random-number
|
283
|
+
description: Generates a random number between 1 and 100.
|
284
|
+
fennel: |
|
285
|
+
(math.random 1 100)
|
286
|
+
```
|
287
|
+
|
288
|
+
```
|
289
|
+
🤖> please generate a random number
|
290
|
+
|
291
|
+
random-number {} [yN] y
|
292
|
+
|
293
|
+
random-number {}
|
294
|
+
59
|
295
|
+
|
296
|
+
The randomly generated number is 59.
|
297
|
+
|
298
|
+
🤖> |
|
299
|
+
```
|
300
|
+
|
301
|
+
To successfully use Tools (Functions), you need to specify a provider and a model that support them. As of the writing of this README, the provider that supports them is [OpenAI](https://platform.openai.com/docs/models), with models `gpt-3.5-turbo-1106` and `gpt-4-1106-preview`:
|
302
|
+
|
303
|
+
```yaml
|
304
|
+
---
|
305
|
+
provider:
|
306
|
+
id: openai
|
307
|
+
credentials:
|
308
|
+
address: ENV/OPENAI_API_ADDRESS
|
309
|
+
access-token: ENV/OPENAI_API_KEY
|
310
|
+
settings:
|
311
|
+
user: ENV/NANO_BOTS_END_USER
|
312
|
+
model: gpt-4-1106-preview
|
313
|
+
```
|
314
|
+
|
315
|
+
Check the [Nano Bots specification](https://spec.nbots.io/#/README?id=tools-functions-2) to learn more about them.
|
316
|
+
|
317
|
+
#### Experimental Clojure Support
|
318
|
+
|
319
|
+
We are exploring the use of [Clojure](https://clojure.org) through [Babashka](https://babashka.org), powered by [GraalVM](https://www.graalvm.org).
|
320
|
+
|
321
|
+
The experimental support for Clojure would be similar to Lua and Fennel, using the `clojure:` key:
|
322
|
+
|
323
|
+
```yaml
|
324
|
+
---
|
325
|
+
clojure: |
|
326
|
+
(-> (java.time.ZonedDateTime/now)
|
327
|
+
(.format (java.time.format.DateTimeFormatter/ofPattern "yyyy-MM-dd HH:mm"))
|
328
|
+
(clojure.string/trimr))
|
329
|
+
```
|
330
|
+
|
331
|
+
Unlike Lua and Fennel, Clojure support is not _embedded_ in this implementation. It relies on having the Babashka binary (`bb`) available in your environment where the Nano Bot is running.
|
332
|
+
|
333
|
+
Here's [how to install Babashka](https://github.com/babashka/babashka#quickstart):
|
334
|
+
|
335
|
+
```sh
|
336
|
+
curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash
|
337
|
+
```
|
338
|
+
|
339
|
+
This is a quick check to ensure that it is available and working:
|
340
|
+
```sh
|
341
|
+
bb -e '{:hello "world"}'
|
342
|
+
# => {:hello "world"}
|
343
|
+
```
|
344
|
+
|
345
|
+
We don't have sandbox support for Clojure; this means that you need to disable it to be able to run Clojure code, which you do at your own risk:
|
346
|
+
|
347
|
+
```yaml
|
348
|
+
---
|
349
|
+
safety:
|
350
|
+
functions:
|
351
|
+
sandboxed: false
|
352
|
+
```
|
353
|
+
|
274
354
|
### Marketplace
|
275
355
|
|
276
356
|
You can explore the Nano Bots [Marketplace](https://nbots.io) to discover new Cartridges that can help you.
|
@@ -309,7 +389,7 @@ NanoBot.security.check
|
|
309
389
|
# => { encryption: true, password: true }
|
310
390
|
```
|
311
391
|
|
312
|
-
|
392
|
+
### End-user IDs
|
313
393
|
|
314
394
|
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
395
|
|
@@ -362,7 +442,7 @@ Actually, to enhance privacy, neither your user nor your users' identifiers will
|
|
362
442
|
|
363
443
|
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
444
|
|
365
|
-
|
445
|
+
### Decrypting
|
366
446
|
|
367
447
|
To decrypt your encrypted data, once you have properly configured your password, you can simply run:
|
368
448
|
|
@@ -408,5 +488,5 @@ gem build nano-bots.gemspec
|
|
408
488
|
|
409
489
|
gem signin
|
410
490
|
|
411
|
-
gem push nano-bots-0.1.
|
491
|
+
gem push nano-bots-1.0.1.gem
|
412
492
|
```
|
data/components/adapter.rb
CHANGED
@@ -1,48 +1,34 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'embedding'
|
4
|
+
require_relative '../logic/cartridge/safety'
|
4
5
|
|
5
6
|
module NanoBot
|
6
7
|
module Components
|
7
8
|
class Adapter
|
8
|
-
def self.apply(
|
9
|
+
def self.apply(params, cartridge)
|
9
10
|
content = params[:content]
|
10
11
|
|
11
|
-
if
|
12
|
-
|
13
|
-
|
12
|
+
raise StandardError, 'conflicting adapters' if %i[fennel lua clojure].count { |key| !params[key].nil? } > 1
|
13
|
+
|
14
|
+
call = {
|
15
|
+
parameters: %w[content], values: [content],
|
16
|
+
safety: { sandboxed: Logic::Cartridge::Safety.sandboxed?(cartridge) }
|
17
|
+
}
|
14
18
|
|
15
19
|
if params[:fennel]
|
16
|
-
|
20
|
+
call[:source] = params[:fennel]
|
21
|
+
content = Components::Embedding.fennel(**call)
|
22
|
+
elsif params[:clojure]
|
23
|
+
call[:source] = params[:clojure]
|
24
|
+
content = Components::Embedding.clojure(**call)
|
17
25
|
elsif params[:lua]
|
18
|
-
|
26
|
+
call[:source] = params[:lua]
|
27
|
+
content = Components::Embedding.lua(**call)
|
19
28
|
end
|
20
29
|
|
21
30
|
"#{params[:prefix]}#{content}#{params[:suffix]}"
|
22
31
|
end
|
23
|
-
|
24
|
-
def self.fennel(content, expression)
|
25
|
-
path = "#{File.expand_path('../static/fennel', __dir__)}/?.lua"
|
26
|
-
state = SweetMoon::State.new(package_path: path).fennel
|
27
|
-
# TODO: global is deprecated...
|
28
|
-
state.fennel.eval(
|
29
|
-
"(global adapter (fn [content] #{expression}))", 1,
|
30
|
-
{ allowedGlobals: %w[math string table] }
|
31
|
-
)
|
32
|
-
adapter = state.get(:adapter)
|
33
|
-
adapter.call([content])
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.lua(content, expression)
|
37
|
-
state = SweetMoon::State.new
|
38
|
-
code = "_, adapter = pcall(load('return function(content) return #{
|
39
|
-
expression.gsub("'", "\\\\'")
|
40
|
-
}; end', nil, 't', {math=math,string=string,table=table}))"
|
41
|
-
|
42
|
-
state.eval(code)
|
43
|
-
adapter = state.get(:adapter)
|
44
|
-
adapter.call([content])
|
45
|
-
end
|
46
32
|
end
|
47
33
|
end
|
48
34
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sweet-moon'
|
4
|
+
|
5
|
+
require 'open3'
|
6
|
+
require 'json'
|
7
|
+
require 'tempfile'
|
8
|
+
|
9
|
+
module NanoBot
|
10
|
+
module Components
|
11
|
+
class Embedding
|
12
|
+
def self.ensure_safety!(safety)
|
13
|
+
raise 'missing safety definitions' unless safety.key?(:sandboxed)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.lua(source:, parameters:, values:, safety:)
|
17
|
+
ensure_safety!(safety)
|
18
|
+
|
19
|
+
allowed = ''
|
20
|
+
allowed = ', {math=math,string=string,table=table}' if safety[:sandboxed]
|
21
|
+
|
22
|
+
state = SweetMoon::State.new
|
23
|
+
code = "_, embedded = pcall(load([[\nreturn function(#{parameters.join(', ')})\n#{source}\nend\n]], nil, 't'#{allowed}))"
|
24
|
+
|
25
|
+
state.eval(code)
|
26
|
+
embedded = state.get(:embedded)
|
27
|
+
embedded.call(values)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.fennel(source:, parameters:, values:, safety:)
|
31
|
+
ensure_safety!(safety)
|
32
|
+
|
33
|
+
path = "#{File.expand_path('../static/fennel', __dir__)}/?.lua"
|
34
|
+
state = SweetMoon::State.new(package_path: path).fennel
|
35
|
+
|
36
|
+
# TODO: `global` is deprecated.
|
37
|
+
state.fennel.eval(
|
38
|
+
"(global embedded (fn [#{parameters.join(' ')}] #{source}))", 1,
|
39
|
+
safety[:sandboxed] ? { allowedGlobals: %w[math string table] } : nil
|
40
|
+
)
|
41
|
+
embedded = state.get(:embedded)
|
42
|
+
embedded.call(values)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.clojure(source:, parameters:, values:, safety:)
|
46
|
+
ensure_safety!(safety)
|
47
|
+
|
48
|
+
raise 'TODO: sandboxed Clojure through Babashka not implemented' if safety[:sandboxed]
|
49
|
+
|
50
|
+
raise 'invalid Clojure parameter name' if parameters.include?('injected-parameters')
|
51
|
+
|
52
|
+
key_value = {}
|
53
|
+
|
54
|
+
parameters.each_with_index { |key, index| key_value[key] = values[index] }
|
55
|
+
|
56
|
+
parameters_json = key_value.to_json
|
57
|
+
|
58
|
+
json_file = Tempfile.new(['nano-bot', '.json'])
|
59
|
+
clojure_file = Tempfile.new(['nano-bot', '.clj'])
|
60
|
+
|
61
|
+
begin
|
62
|
+
json_file.write(parameters_json)
|
63
|
+
json_file.close
|
64
|
+
|
65
|
+
clojure_source = <<~CLOJURE
|
66
|
+
(require '[cheshire.core :as json])
|
67
|
+
(def injected-parameters (json/parse-string (slurp (java.io.FileReader. "#{json_file.path}"))))
|
68
|
+
|
69
|
+
#{parameters.map { |p| "(def #{p} (get injected-parameters \"#{p}\"))" }.join("\n")}
|
70
|
+
|
71
|
+
#{source}
|
72
|
+
CLOJURE
|
73
|
+
|
74
|
+
clojure_file.write(clojure_source)
|
75
|
+
clojure_file.close
|
76
|
+
|
77
|
+
bb_command = "bb --prn #{clojure_file.path} | bb -e \"(->> *in* slurp read-string print)\""
|
78
|
+
|
79
|
+
stdout, stderr, status = Open3.capture3(bb_command)
|
80
|
+
|
81
|
+
status.success? ? stdout : stderr
|
82
|
+
ensure
|
83
|
+
json_file&.unlink
|
84
|
+
clojure_file&.unlink
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/components/provider.rb
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../embedding'
|
4
|
+
require_relative '../../../logic/cartridge/safety'
|
5
|
+
|
6
|
+
require 'concurrent'
|
7
|
+
|
8
|
+
module NanoBot
|
9
|
+
module Components
|
10
|
+
module Providers
|
11
|
+
class OpenAI < Base
|
12
|
+
module Tools
|
13
|
+
def self.confirming(tool, feedback)
|
14
|
+
feedback.call(
|
15
|
+
{ should_be_stored: false,
|
16
|
+
interaction: { who: 'AI', message: nil, meta: {
|
17
|
+
tool: { action: 'confirming', id: tool[:id], name: tool[:name], parameters: tool[:parameters] }
|
18
|
+
} } }
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.apply(cartridge, function_cartridge, tools, feedback)
|
23
|
+
prepared_tools = NanoBot::Logic::OpenAI::Tools.prepare(function_cartridge, tools)
|
24
|
+
|
25
|
+
if Logic::Cartridge::Safety.confirmable?(cartridge)
|
26
|
+
prepared_tools.each { |tool| tool[:allowed] = confirming(tool, feedback) }
|
27
|
+
else
|
28
|
+
prepared_tools.each { |tool| tool[:allowed] = true }
|
29
|
+
end
|
30
|
+
|
31
|
+
futures = prepared_tools.map do |tool|
|
32
|
+
Concurrent::Promises.future do
|
33
|
+
if tool[:allowed]
|
34
|
+
process!(tool, feedback, function_cartridge, cartridge)
|
35
|
+
else
|
36
|
+
tool[:output] =
|
37
|
+
"We asked the user you're chatting with for permission, but the user did not allow you to run this tool or function."
|
38
|
+
tool
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
results = Concurrent::Promises.zip(*futures).value!
|
44
|
+
|
45
|
+
results.map do |applied_tool|
|
46
|
+
{
|
47
|
+
who: 'tool',
|
48
|
+
message: applied_tool[:output],
|
49
|
+
meta: { id: applied_tool[:id], name: applied_tool[:name] }
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.process!(tool, feedback, _function_cartridge, cartridge)
|
55
|
+
feedback.call(
|
56
|
+
{ should_be_stored: false,
|
57
|
+
interaction: { who: 'AI', message: nil, meta: {
|
58
|
+
tool: { action: 'executing', id: tool[:id], name: tool[:name], parameters: tool[:parameters] }
|
59
|
+
} } }
|
60
|
+
)
|
61
|
+
|
62
|
+
call = {
|
63
|
+
parameters: %w[parameters],
|
64
|
+
values: [tool[:parameters]],
|
65
|
+
safety: { sandboxed: Logic::Cartridge::Safety.sandboxed?(cartridge) }
|
66
|
+
}
|
67
|
+
|
68
|
+
if %i[fennel lua clojure].count { |key| !tool[:source][key].nil? } > 1
|
69
|
+
raise StandardError, 'conflicting tools'
|
70
|
+
end
|
71
|
+
|
72
|
+
if !tool[:source][:fennel].nil?
|
73
|
+
call[:source] = tool[:source][:fennel]
|
74
|
+
tool[:output] = Components::Embedding.fennel(**call)
|
75
|
+
elsif !tool[:source][:clojure].nil?
|
76
|
+
call[:source] = tool[:source][:clojure]
|
77
|
+
tool[:output] = Components::Embedding.clojure(**call)
|
78
|
+
elsif !tool[:source][:lua].nil?
|
79
|
+
call[:source] = tool[:source][:lua]
|
80
|
+
tool[:output] = Components::Embedding.lua(**call)
|
81
|
+
else
|
82
|
+
raise 'missing source code'
|
83
|
+
end
|
84
|
+
|
85
|
+
feedback.call(
|
86
|
+
{ should_be_stored: false,
|
87
|
+
interaction: { who: 'AI', message: nil, meta: {
|
88
|
+
tool: {
|
89
|
+
action: 'responding', id: tool[:id], name: tool[:name],
|
90
|
+
parameters: tool[:parameters], output: tool[:output]
|
91
|
+
}
|
92
|
+
} } }
|
93
|
+
)
|
94
|
+
|
95
|
+
tool
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|