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
@@ -2,9 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'openai'
|
4
4
|
|
5
|
-
require_relative '
|
5
|
+
require_relative 'base'
|
6
6
|
require_relative '../crypto'
|
7
7
|
|
8
|
+
require_relative '../../logic/providers/openai/tools'
|
9
|
+
require_relative '../../controllers/interfaces/tools'
|
10
|
+
|
11
|
+
require_relative 'openai/tools'
|
12
|
+
|
8
13
|
module NanoBot
|
9
14
|
module Components
|
10
15
|
module Providers
|
@@ -32,17 +37,16 @@ module NanoBot
|
|
32
37
|
@client = ::OpenAI::Client.new(uri_base:, access_token: @credentials[:'access-token'])
|
33
38
|
end
|
34
39
|
|
35
|
-
def
|
36
|
-
provider = @settings.key?(:stream) ? @settings[:stream] : true
|
37
|
-
interface = input[:interface].key?(:stream) ? input[:interface][:stream] : true
|
38
|
-
|
39
|
-
provider && interface
|
40
|
-
end
|
41
|
-
|
42
|
-
def evaluate(input, &block)
|
40
|
+
def evaluate(input, streaming, cartridge, &feedback)
|
43
41
|
messages = input[:history].map do |event|
|
44
|
-
|
45
|
-
content: event[:
|
42
|
+
if event[:message].nil? && event[:meta] && event[:meta][:tool_calls]
|
43
|
+
{ role: 'assistant', content: nil, tool_calls: event[:meta][:tool_calls] }
|
44
|
+
elsif event[:who] == 'tool'
|
45
|
+
{ role: event[:who], content: event[:message].to_s,
|
46
|
+
tool_call_id: event[:meta][:id], name: event[:meta][:name] }
|
47
|
+
else
|
48
|
+
{ role: event[:who] == 'user' ? 'user' : 'assistant', content: event[:message] }
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
52
|
%i[instruction backdrop directive].each do |key|
|
@@ -62,26 +66,105 @@ module NanoBot
|
|
62
66
|
|
63
67
|
payload.delete(:logit_bias) if payload.key?(:logit_bias) && payload[:logit_bias].nil?
|
64
68
|
|
65
|
-
if
|
69
|
+
payload[:tools] = input[:tools].map { |raw| NanoBot::Logic::OpenAI::Tools.adapt(raw) } if input[:tools]
|
70
|
+
|
71
|
+
if streaming
|
66
72
|
content = ''
|
73
|
+
tools = []
|
67
74
|
|
68
75
|
payload[:stream] = proc do |chunk, _bytesize|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
76
|
+
partial_content = chunk.dig('choices', 0, 'delta', 'content')
|
77
|
+
partial_tools = chunk.dig('choices', 0, 'delta', 'tool_calls')
|
78
|
+
|
79
|
+
if partial_tools
|
80
|
+
partial_tools.each do |partial_tool|
|
81
|
+
tools[partial_tool['index']] = {} if tools[partial_tool['index']].nil?
|
82
|
+
|
83
|
+
partial_tool.keys.reject { |key| ['index'].include?(key) }.each do |key|
|
84
|
+
target = tools[partial_tool['index']]
|
85
|
+
|
86
|
+
if partial_tool[key].is_a?(Hash)
|
87
|
+
target[key] = {} if target[key].nil?
|
88
|
+
partial_tool[key].each_key do |sub_key|
|
89
|
+
target[key][sub_key] = '' if target[key][sub_key].nil?
|
90
|
+
|
91
|
+
target[key][sub_key] += partial_tool[key][sub_key]
|
92
|
+
end
|
93
|
+
else
|
94
|
+
target[key] = '' if target[key].nil?
|
95
|
+
|
96
|
+
target[key] += partial_tool[key]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
73
100
|
end
|
74
101
|
|
75
|
-
|
102
|
+
if partial_content
|
103
|
+
content += partial_content
|
104
|
+
feedback.call(
|
105
|
+
{ should_be_stored: false,
|
106
|
+
interaction: { who: 'AI', message: partial_content } }
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
if chunk.dig('choices', 0, 'finish_reason')
|
111
|
+
if tools&.size&.positive?
|
112
|
+
feedback.call(
|
113
|
+
{ should_be_stored: true,
|
114
|
+
needs_another_round: true,
|
115
|
+
interaction: { who: 'AI', message: nil, meta: { tool_calls: tools } } }
|
116
|
+
)
|
117
|
+
Tools.apply(cartridge, input[:tools], tools, feedback).each do |interaction|
|
118
|
+
feedback.call({ should_be_stored: true, needs_another_round: true, interaction: })
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
feedback.call(
|
123
|
+
{ should_be_stored: !(content.nil? || content == ''),
|
124
|
+
interaction: content.nil? || content == '' ? nil : { who: 'AI', message: content },
|
125
|
+
finished: true }
|
126
|
+
)
|
127
|
+
end
|
76
128
|
end
|
77
129
|
|
78
|
-
|
130
|
+
begin
|
131
|
+
@client.chat(parameters: payload)
|
132
|
+
rescue StandardError => e
|
133
|
+
raise e.class, e.response[:body] if e.response && e.response[:body]
|
134
|
+
|
135
|
+
raise e
|
136
|
+
end
|
79
137
|
else
|
80
|
-
|
138
|
+
begin
|
139
|
+
result = @client.chat(parameters: payload)
|
140
|
+
rescue StandardError => e
|
141
|
+
raise e.class, e.response[:body] if e.response && e.response[:body]
|
142
|
+
|
143
|
+
raise e
|
144
|
+
end
|
81
145
|
|
82
146
|
raise StandardError, result['error'] if result['error']
|
83
147
|
|
84
|
-
|
148
|
+
tools = result.dig('choices', 0, 'message', 'tool_calls')
|
149
|
+
|
150
|
+
if tools&.size&.positive?
|
151
|
+
feedback.call(
|
152
|
+
{ should_be_stored: true,
|
153
|
+
needs_another_round: true,
|
154
|
+
interaction: { who: 'AI', message: nil, meta: { tool_calls: tools } } }
|
155
|
+
)
|
156
|
+
Tools.apply(cartridge, input[:tools], tools, feedback).each do |interaction|
|
157
|
+
feedback.call({ should_be_stored: true, needs_another_round: true, interaction: })
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
content = result.dig('choices', 0, 'message', 'content')
|
162
|
+
|
163
|
+
feedback.call(
|
164
|
+
{ should_be_stored: !(content.nil? || content == ''),
|
165
|
+
interaction: content.nil? || content == '' ? nil : { who: 'AI', message: content },
|
166
|
+
finished: true }
|
167
|
+
)
|
85
168
|
end
|
86
169
|
end
|
87
170
|
|
data/components/storage.rb
CHANGED
data/components/stream.rb
CHANGED
@@ -7,7 +7,12 @@ module NanoBot
|
|
7
7
|
class Stream < StringIO
|
8
8
|
def write(*args)
|
9
9
|
if @callback
|
10
|
-
|
10
|
+
begin
|
11
|
+
@accumulated += args.first
|
12
|
+
rescue StandardError => _e
|
13
|
+
@accumulated = "#{@accumulated.force_encoding('UTF-8')}#{args.first.force_encoding('UTF-8')}"
|
14
|
+
end
|
15
|
+
|
11
16
|
@callback.call(@accumulated, args.first, false)
|
12
17
|
end
|
13
18
|
super
|
data/controllers/cartridges.rb
CHANGED
@@ -23,7 +23,7 @@ module NanoBot
|
|
23
23
|
|
24
24
|
files.values.uniq.map do |file|
|
25
25
|
cartridge = Logic::Helpers::Hash.symbolize_keys(
|
26
|
-
YAML.
|
26
|
+
YAML.safe_load_file(file[:path], permitted_classes: [Symbol])
|
27
27
|
).merge({
|
28
28
|
system: {
|
29
29
|
id: file[:path].to_s.sub(/^#{Regexp.escape(file[:base])}/, '').sub(%r{^/}, '').sub(/\.[^.]+\z/,
|
data/controllers/instance.rb
CHANGED
@@ -6,9 +6,9 @@ require_relative '../logic/helpers/hash'
|
|
6
6
|
require_relative '../components/provider'
|
7
7
|
require_relative '../components/storage'
|
8
8
|
require_relative '../components/stream'
|
9
|
-
require_relative '
|
10
|
-
require_relative '
|
11
|
-
require_relative '
|
9
|
+
require_relative 'interfaces/repl'
|
10
|
+
require_relative 'interfaces/eval'
|
11
|
+
require_relative 'session'
|
12
12
|
|
13
13
|
module NanoBot
|
14
14
|
module Controllers
|
@@ -83,7 +83,7 @@ module NanoBot
|
|
83
83
|
raise StandardError, "Cartridge file not found: \"#{path}\""
|
84
84
|
end
|
85
85
|
|
86
|
-
@cartridge = YAML.
|
86
|
+
@cartridge = YAML.safe_load_file(elected_path, permitted_classes: [Symbol])
|
87
87
|
end
|
88
88
|
|
89
89
|
@safe_cartridge = Marshal.load(Marshal.dump(@cartridge))
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rainbow'
|
4
|
+
|
5
|
+
require_relative '../../logic/cartridge/tools'
|
6
|
+
require_relative '../../logic/cartridge/safety'
|
7
|
+
require_relative '../../components/embedding'
|
8
|
+
|
9
|
+
module NanoBot
|
10
|
+
module Controllers
|
11
|
+
module Interfaces
|
12
|
+
module Tool
|
13
|
+
def self.confirming(session, cartridge, mode, feedback)
|
14
|
+
yeses = Logic::Cartridge::Safety.yeses(cartridge)
|
15
|
+
default_answer = Logic::Cartridge::Safety.default_answer(cartridge)
|
16
|
+
dispatch_feedback(session, cartridge, mode, feedback)
|
17
|
+
session.flush
|
18
|
+
answer = $stdin.gets.chomp.to_s.downcase.strip
|
19
|
+
answer = default_answer if answer == ''
|
20
|
+
session.print("\n")
|
21
|
+
yeses.include?(answer)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.adapt(feedback, adapter, cartridge)
|
25
|
+
call = {
|
26
|
+
parameters: %w[id name parameters parameters-as-json output],
|
27
|
+
values: [
|
28
|
+
feedback[:id], feedback[:name], feedback[:parameters],
|
29
|
+
feedback[:parameters].to_json,
|
30
|
+
feedback[:output]
|
31
|
+
],
|
32
|
+
safety: { sandboxed: Logic::Cartridge::Safety.sandboxed?(cartridge) }
|
33
|
+
}
|
34
|
+
|
35
|
+
raise StandardError, 'conflicting adapters' if %i[fennel lua clojure].count { |key| !adapter[key].nil? } > 1
|
36
|
+
|
37
|
+
if adapter[:fennel]
|
38
|
+
call[:source] = adapter[:fennel]
|
39
|
+
Components::Embedding.fennel(**call)
|
40
|
+
elsif adapter[:clojure]
|
41
|
+
call[:source] = adapter[:clojure]
|
42
|
+
Components::Embedding.clojure(**call)
|
43
|
+
elsif adapter[:lua]
|
44
|
+
call[:parameters] = %w[id name parameters parameters_as_json output]
|
45
|
+
call[:source] = adapter[:lua]
|
46
|
+
Components::Embedding.lua(**call)
|
47
|
+
else
|
48
|
+
raise 'missing handler for adapter'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.dispatch_feedback(session, cartridge, mode, feedback)
|
53
|
+
enabled = Logic::Cartridge::Tools.feedback?(cartridge, mode.to_sym, feedback[:action].to_sym)
|
54
|
+
|
55
|
+
enabled = true if feedback[:action].to_sym == :confirming
|
56
|
+
|
57
|
+
return unless enabled
|
58
|
+
|
59
|
+
color = Logic::Cartridge::Tools.fetch_from_interface(
|
60
|
+
cartridge, mode.to_sym, feedback[:action].to_sym, [:color]
|
61
|
+
)
|
62
|
+
|
63
|
+
adapter = Tool.adapter(cartridge, mode, feedback)
|
64
|
+
|
65
|
+
if %i[fennel lua clojure].any? { |key| !adapter[key].nil? }
|
66
|
+
message = adapt(feedback, adapter, cartridge)
|
67
|
+
else
|
68
|
+
message = "#{feedback[:name]} #{feedback[:parameters].to_json}"
|
69
|
+
|
70
|
+
message += "\n#{feedback[:output]}" if feedback[:action].to_sym == :responding
|
71
|
+
end
|
72
|
+
|
73
|
+
message = "#{adapter[:prefix]}#{message}#{adapter[:suffix]}"
|
74
|
+
|
75
|
+
session.print(color.nil? ? message : Rainbow(message).send(color))
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.adapter(cartridge, mode, feedback)
|
79
|
+
prefix = Logic::Cartridge::Tools.fetch_from_interface(
|
80
|
+
cartridge, mode.to_sym, feedback[:action].to_sym, [:prefix]
|
81
|
+
)
|
82
|
+
|
83
|
+
suffix = Logic::Cartridge::Tools.fetch_from_interface(
|
84
|
+
cartridge, mode.to_sym, feedback[:action].to_sym, [:suffix]
|
85
|
+
)
|
86
|
+
|
87
|
+
fennel = Logic::Cartridge::Tools.fetch_from_interface(
|
88
|
+
cartridge, mode.to_sym, feedback[:action].to_sym, %i[adapter fennel]
|
89
|
+
)
|
90
|
+
|
91
|
+
lua = Logic::Cartridge::Tools.fetch_from_interface(
|
92
|
+
cartridge, mode.to_sym, feedback[:action].to_sym, %i[adapter lua]
|
93
|
+
)
|
94
|
+
|
95
|
+
clojure = Logic::Cartridge::Tools.fetch_from_interface(
|
96
|
+
cartridge, mode.to_sym, feedback[:action].to_sym, %i[adapter clojure]
|
97
|
+
)
|
98
|
+
|
99
|
+
{ prefix:, suffix:, fennel:, lua:, clojure: }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/controllers/security.rb
CHANGED
@@ -18,11 +18,10 @@ module NanoBot
|
|
18
18
|
password = 'UNSAFE' unless password && password != ''
|
19
19
|
|
20
20
|
{
|
21
|
-
encryption:
|
21
|
+
encryption:
|
22
22
|
Components::Crypto.encrypt('SAFE') != 'SAFE' &&
|
23
|
-
|
24
|
-
|
25
|
-
),
|
23
|
+
Components::Crypto.encrypt('SAFE') != Components::Crypto.encrypt('SAFE') &&
|
24
|
+
Components::Crypto.decrypt(Components::Crypto.encrypt('SAFE')) == 'SAFE',
|
26
25
|
password: password != 'UNSAFE'
|
27
26
|
}
|
28
27
|
end
|
data/controllers/session.rb
CHANGED
@@ -3,10 +3,14 @@
|
|
3
3
|
require 'babosa'
|
4
4
|
|
5
5
|
require 'fileutils'
|
6
|
+
require 'rainbow'
|
6
7
|
|
7
8
|
require_relative '../logic/helpers/hash'
|
9
|
+
require_relative '../logic/cartridge/safety'
|
8
10
|
require_relative '../logic/cartridge/streaming'
|
9
11
|
require_relative '../logic/cartridge/interaction'
|
12
|
+
require_relative '../logic/cartridge/fetch'
|
13
|
+
require_relative 'interfaces/tools'
|
10
14
|
require_relative '../components/storage'
|
11
15
|
require_relative '../components/adapter'
|
12
16
|
require_relative '../components/crypto'
|
@@ -14,6 +18,7 @@ require_relative '../components/crypto'
|
|
14
18
|
module NanoBot
|
15
19
|
module Controllers
|
16
20
|
STREAM_TIMEOUT_IN_SECONDS = 5
|
21
|
+
INFINITE_LOOP_PREVENTION = 10
|
17
22
|
|
18
23
|
class Session
|
19
24
|
attr_accessor :stream
|
@@ -41,9 +46,9 @@ module NanoBot
|
|
41
46
|
end
|
42
47
|
|
43
48
|
def load_state
|
44
|
-
@state = Logic::Helpers::Hash.symbolize_keys(
|
45
|
-
|
46
|
-
|
49
|
+
@state = Logic::Helpers::Hash.symbolize_keys(
|
50
|
+
JSON.parse(Components::Crypto.decrypt(File.read(@state_path)))
|
51
|
+
)
|
47
52
|
end
|
48
53
|
|
49
54
|
def store_state!
|
@@ -68,7 +73,7 @@ module NanoBot
|
|
68
73
|
mode: mode.to_s,
|
69
74
|
input: message,
|
70
75
|
message: Components::Adapter.apply(
|
71
|
-
|
76
|
+
Logic::Cartridge::Interaction.input(@cartridge, mode.to_sym, message), @cartridge
|
72
77
|
)
|
73
78
|
}
|
74
79
|
|
@@ -78,41 +83,88 @@ module NanoBot
|
|
78
83
|
end
|
79
84
|
|
80
85
|
def process(input, mode:)
|
86
|
+
interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
|
87
|
+
|
88
|
+
input[:interface] = interface
|
89
|
+
input[:tools] = @cartridge[:tools]
|
90
|
+
|
91
|
+
needs_another_round = true
|
92
|
+
|
93
|
+
rounds = 0
|
94
|
+
|
95
|
+
while needs_another_round
|
96
|
+
needs_another_round = process_interaction(input, mode:)
|
97
|
+
rounds += 1
|
98
|
+
raise StandardError, 'infinite loop prevention' if rounds > INFINITE_LOOP_PREVENTION
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def process_interaction(input, mode:)
|
81
103
|
prefix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :prefix)
|
82
104
|
suffix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :suffix)
|
83
105
|
|
84
|
-
|
106
|
+
color = Logic::Cartridge::Fetch.cascate(
|
107
|
+
@cartridge, [[:interfaces, mode.to_sym, :output, :color], %i[interfaces output color]]
|
108
|
+
)
|
85
109
|
|
86
|
-
|
110
|
+
color = color.to_sym if color
|
87
111
|
|
88
|
-
|
112
|
+
streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym)
|
89
113
|
|
90
114
|
updated_at = Time.now
|
91
115
|
|
92
116
|
ready = false
|
93
|
-
@provider.evaluate(input) do |output, finished|
|
94
|
-
updated_at = Time.now
|
95
|
-
|
96
|
-
if finished
|
97
|
-
event = Marshal.load(Marshal.dump(output))
|
98
117
|
|
99
|
-
|
100
|
-
@cartridge, mode.to_sym, output, streaming, finished
|
101
|
-
)
|
118
|
+
needs_another_round = false
|
102
119
|
|
103
|
-
|
120
|
+
@provider.evaluate(input, streaming, @cartridge) do |feedback|
|
121
|
+
needs_another_round = true if feedback[:needs_another_round]
|
104
122
|
|
105
|
-
|
106
|
-
event[:output] = "#{prefix}#{output[:message]}#{suffix}"
|
107
|
-
|
108
|
-
@state[:history] << event
|
109
|
-
|
110
|
-
self.print(output[:message]) unless streaming
|
123
|
+
updated_at = Time.now
|
111
124
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
125
|
+
if feedback[:interaction] &&
|
126
|
+
feedback.dig(:interaction, :meta, :tool, :action) &&
|
127
|
+
feedback[:interaction][:meta][:tool][:action] == 'confirming'
|
128
|
+
Interfaces::Tool.confirming(self, @cartridge, mode, feedback[:interaction][:meta][:tool])
|
129
|
+
else
|
130
|
+
|
131
|
+
if feedback[:interaction] && feedback.dig(:interaction, :meta, :tool, :action)
|
132
|
+
Interfaces::Tool.dispatch_feedback(
|
133
|
+
self, @cartridge, mode, feedback[:interaction][:meta][:tool]
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
if feedback[:interaction]
|
138
|
+
event = Marshal.load(Marshal.dump(feedback[:interaction]))
|
139
|
+
event[:mode] = mode.to_s
|
140
|
+
event[:output] = nil
|
141
|
+
|
142
|
+
if feedback[:interaction][:who] == 'AI' && feedback[:interaction][:message]
|
143
|
+
event[:output] = feedback[:interaction][:message]
|
144
|
+
unless streaming
|
145
|
+
output = Logic::Cartridge::Interaction.output(
|
146
|
+
@cartridge, mode.to_sym, feedback[:interaction], streaming, feedback[:finished]
|
147
|
+
)
|
148
|
+
output[:message] = Components::Adapter.apply(output[:message], @cartridge)
|
149
|
+
event[:output] = (output[:message]).to_s
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@state[:history] << event if feedback[:should_be_stored]
|
154
|
+
|
155
|
+
if event[:output] && ((!feedback[:finished] && streaming) || (!streaming && feedback[:finished]))
|
156
|
+
self.print(color ? Rainbow(event[:output]).send(color) : event[:output])
|
157
|
+
end
|
158
|
+
|
159
|
+
# The `print` function already outputs a prefix and a suffix, so
|
160
|
+
# we should add them afterwards to avoid printing them twice.
|
161
|
+
event[:output] = "#{prefix}#{event[:output]}#{suffix}"
|
162
|
+
end
|
163
|
+
|
164
|
+
if feedback[:finished]
|
165
|
+
flush
|
166
|
+
ready = true
|
167
|
+
end
|
116
168
|
end
|
117
169
|
end
|
118
170
|
|
@@ -122,6 +174,8 @@ module NanoBot
|
|
122
174
|
end
|
123
175
|
|
124
176
|
store_state! unless @stateless
|
177
|
+
|
178
|
+
needs_another_round
|
125
179
|
end
|
126
180
|
|
127
181
|
def flush
|
data/docker-compose.example.yml
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
---
|
3
2
|
services:
|
4
3
|
nano-bots:
|
5
|
-
image: ruby:3.2.2-slim-
|
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
|
4
|
+
image: ruby:3.2.2-slim-bookworm
|
5
|
+
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"
|
7
6
|
environment:
|
8
7
|
OPENAI_API_ADDRESS: https://api.openai.com
|
9
8
|
OPENAI_API_KEY: your-access-token
|
data/logic/cartridge/adapters.rb
CHANGED
data/logic/cartridge/affixes.rb
CHANGED
data/logic/cartridge/default.rb
CHANGED
@@ -15,7 +15,7 @@ module NanoBot
|
|
15
15
|
return @values if @values
|
16
16
|
|
17
17
|
path = File.expand_path('../../static/cartridges/default.yml', __dir__)
|
18
|
-
cartridge = YAML.
|
18
|
+
cartridge = YAML.safe_load_file(path, permitted_classes: [Symbol])
|
19
19
|
@values = Logic::Helpers::Hash.symbolize_keys(cartridge)
|
20
20
|
@values
|
21
21
|
end
|
@@ -24,7 +24,7 @@ module NanoBot
|
|
24
24
|
return @baseline if @baseline
|
25
25
|
|
26
26
|
path = File.expand_path('../../static/cartridges/baseline.yml', __dir__)
|
27
|
-
cartridge = YAML.
|
27
|
+
cartridge = YAML.safe_load_file(path, permitted_classes: [Symbol])
|
28
28
|
@baseline = Logic::Helpers::Hash.symbolize_keys(cartridge)
|
29
29
|
@baseline
|
30
30
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'default'
|
4
|
+
require_relative '../helpers/hash'
|
5
|
+
|
6
|
+
module NanoBot
|
7
|
+
module Logic
|
8
|
+
module Cartridge
|
9
|
+
module Fetch
|
10
|
+
def self.cascate(cartridge, paths)
|
11
|
+
results = paths.map { |path| Helpers::Hash.fetch(cartridge, path) }
|
12
|
+
result = results.find { |candidate| !candidate.nil? }
|
13
|
+
return result unless result.nil?
|
14
|
+
|
15
|
+
results = paths.map { |path| Helpers::Hash.fetch(Default.instance.values, path) }
|
16
|
+
result = results.find { |candidate| !candidate.nil? }
|
17
|
+
return result unless result.nil?
|
18
|
+
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require_relative './affixes'
|
6
|
-
require_relative './adapters'
|
3
|
+
require_relative 'affixes'
|
4
|
+
require_relative 'adapters'
|
7
5
|
|
8
6
|
module NanoBot
|
9
7
|
module Logic
|
@@ -12,23 +10,25 @@ module NanoBot
|
|
12
10
|
def self.input(cartridge, interface, content)
|
13
11
|
lua = Adapter.expression(cartridge, interface, :input, :lua)
|
14
12
|
fennel = Adapter.expression(cartridge, interface, :input, :fennel)
|
13
|
+
clojure = Adapter.expression(cartridge, interface, :input, :clojure)
|
15
14
|
|
16
15
|
prefix = Affixes.get(cartridge, interface, :input, :prefix)
|
17
16
|
suffix = Affixes.get(cartridge, interface, :input, :suffix)
|
18
17
|
|
19
|
-
{ content:, prefix:, suffix:, lua:, fennel: }
|
18
|
+
{ content:, prefix:, suffix:, lua:, fennel:, clojure: }
|
20
19
|
end
|
21
20
|
|
22
21
|
def self.output(cartridge, interface, result, streaming, _finished)
|
23
22
|
if streaming
|
24
|
-
result[:message] = { content: result[:message], lua: nil, fennel: nil }
|
23
|
+
result[:message] = { content: result[:message], lua: nil, fennel: nil, clojure: nil }
|
25
24
|
return result
|
26
25
|
end
|
27
26
|
|
28
27
|
lua = Adapter.expression(cartridge, interface, :output, :lua)
|
29
28
|
fennel = Adapter.expression(cartridge, interface, :output, :fennel)
|
29
|
+
clojure = Adapter.expression(cartridge, interface, :output, :clojure)
|
30
30
|
|
31
|
-
result[:message] = { content: result[:message], lua:, fennel: }
|
31
|
+
result[:message] = { content: result[:message], lua:, fennel:, clojure: }
|
32
32
|
|
33
33
|
result
|
34
34
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'fetch'
|
4
|
+
|
5
|
+
module NanoBot
|
6
|
+
module Logic
|
7
|
+
module Cartridge
|
8
|
+
module Safety
|
9
|
+
def self.default_answer(cartridge)
|
10
|
+
default = Fetch.cascate(cartridge, [%i[interfaces tools confirming default]])
|
11
|
+
return [] if default.nil?
|
12
|
+
|
13
|
+
default
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.yeses(cartridge)
|
17
|
+
yeses_values = Fetch.cascate(cartridge, [%i[interfaces tools confirming yeses]])
|
18
|
+
return [] if yeses_values.nil?
|
19
|
+
|
20
|
+
yeses_values
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.confirmable?(cartridge)
|
24
|
+
confirmable = Fetch.cascate(cartridge, [%i[safety tools confirmable]])
|
25
|
+
return true if confirmable.nil?
|
26
|
+
|
27
|
+
confirmable
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.sandboxed?(cartridge)
|
31
|
+
sandboxed = Fetch.cascate(cartridge, [%i[safety functions sandboxed]])
|
32
|
+
return true if sandboxed.nil?
|
33
|
+
|
34
|
+
sandboxed
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|