summaryrefslogtreecommitdiff
path: root/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'controllers')
-rw-r--r--controllers/cartridges.rb2
-rw-r--r--controllers/instance.rb8
-rw-r--r--controllers/interfaces/cli.rb2
-rw-r--r--controllers/interfaces/tools.rb104
-rw-r--r--controllers/session.rb106
5 files changed, 190 insertions, 32 deletions
diff --git a/controllers/cartridges.rb b/controllers/cartridges.rb
index fe0d56e..df474a9 100644
--- a/controllers/cartridges.rb
+++ b/controllers/cartridges.rb
@@ -23,7 +23,7 @@ module NanoBot
files.values.uniq.map do |file|
cartridge = Logic::Helpers::Hash.symbolize_keys(
- YAML.safe_load(File.read(file[:path]), permitted_classes: [Symbol])
+ YAML.safe_load_file(file[:path], permitted_classes: [Symbol])
).merge({
system: {
id: file[:path].to_s.sub(/^#{Regexp.escape(file[:base])}/, '').sub(%r{^/}, '').sub(/\.[^.]+\z/,
diff --git a/controllers/instance.rb b/controllers/instance.rb
index d4e0c1b..259a548 100644
--- a/controllers/instance.rb
+++ b/controllers/instance.rb
@@ -6,9 +6,9 @@ require_relative '../logic/helpers/hash'
require_relative '../components/provider'
require_relative '../components/storage'
require_relative '../components/stream'
-require_relative './interfaces/repl'
-require_relative './interfaces/eval'
-require_relative './session'
+require_relative 'interfaces/repl'
+require_relative 'interfaces/eval'
+require_relative 'session'
module NanoBot
module Controllers
@@ -83,7 +83,7 @@ module NanoBot
raise StandardError, "Cartridge file not found: \"#{path}\""
end
- @cartridge = YAML.safe_load(File.read(elected_path), permitted_classes: [Symbol])
+ @cartridge = YAML.safe_load_file(elected_path, permitted_classes: [Symbol])
end
@safe_cartridge = Marshal.load(Marshal.dump(@cartridge))
diff --git a/controllers/interfaces/cli.rb b/controllers/interfaces/cli.rb
index ae066cd..5b39c2a 100644
--- a/controllers/interfaces/cli.rb
+++ b/controllers/interfaces/cli.rb
@@ -81,7 +81,7 @@ module NanoBot
when 'cartridge'
puts YAML.dump(bot.cartridge)
else
- raise "TODO: [#{params[:command]}]"
+ raise "Command not found: [#{params[:command]}]"
end
end
end
diff --git a/controllers/interfaces/tools.rb b/controllers/interfaces/tools.rb
new file mode 100644
index 0000000..f079a2b
--- /dev/null
+++ b/controllers/interfaces/tools.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+require 'rainbow'
+
+require_relative '../../logic/cartridge/tools'
+require_relative '../../logic/cartridge/safety'
+require_relative '../../components/embedding'
+
+module NanoBot
+ module Controllers
+ module Interfaces
+ module Tool
+ def self.confirming(session, cartridge, mode, feedback)
+ yeses = Logic::Cartridge::Safety.yeses(cartridge)
+ default_answer = Logic::Cartridge::Safety.default_answer(cartridge)
+ dispatch_feedback(session, cartridge, mode, feedback)
+ session.flush
+ answer = $stdin.gets.chomp.to_s.downcase.strip
+ answer = default_answer if answer == ''
+ session.print("\n")
+ yeses.include?(answer)
+ end
+
+ def self.adapt(feedback, adapter, cartridge)
+ call = {
+ parameters: %w[id name parameters parameters-as-json output],
+ values: [
+ feedback[:id], feedback[:name], feedback[:parameters],
+ feedback[:parameters].to_json,
+ feedback[:output]
+ ],
+ safety: { sandboxed: Logic::Cartridge::Safety.sandboxed?(cartridge) }
+ }
+
+ raise StandardError, 'conflicting adapters' if %i[fennel lua clojure].count { |key| !adapter[key].nil? } > 1
+
+ if adapter[:fennel]
+ call[:source] = adapter[:fennel]
+ Components::Embedding.fennel(**call)
+ elsif adapter[:clojure]
+ call[:source] = adapter[:clojure]
+ Components::Embedding.clojure(**call)
+ elsif adapter[:lua]
+ call[:parameters] = %w[id name parameters parameters_as_json output]
+ call[:source] = adapter[:lua]
+ Components::Embedding.lua(**call)
+ else
+ raise 'missing handler for adapter'
+ end
+ end
+
+ def self.dispatch_feedback(session, cartridge, mode, feedback)
+ enabled = Logic::Cartridge::Tools.feedback?(cartridge, mode.to_sym, feedback[:action].to_sym)
+
+ enabled = true if feedback[:action].to_sym == :confirming
+
+ return unless enabled
+
+ color = Logic::Cartridge::Tools.fetch_from_interface(
+ cartridge, mode.to_sym, feedback[:action].to_sym, [:color]
+ )
+
+ adapter = Tool.adapter(cartridge, mode, feedback)
+
+ if %i[fennel lua clojure].any? { |key| !adapter[key].nil? }
+ message = adapt(feedback, adapter, cartridge)
+ else
+ message = "#{feedback[:name]} #{feedback[:parameters].to_json}"
+
+ message += "\n#{feedback[:output]}" if feedback[:action].to_sym == :responding
+ end
+
+ message = "#{adapter[:prefix]}#{message}#{adapter[:suffix]}"
+
+ session.print(color.nil? ? message : Rainbow(message).send(color))
+ end
+
+ def self.adapter(cartridge, mode, feedback)
+ prefix = Logic::Cartridge::Tools.fetch_from_interface(
+ cartridge, mode.to_sym, feedback[:action].to_sym, [:prefix]
+ )
+
+ suffix = Logic::Cartridge::Tools.fetch_from_interface(
+ cartridge, mode.to_sym, feedback[:action].to_sym, [:suffix]
+ )
+
+ fennel = Logic::Cartridge::Tools.fetch_from_interface(
+ cartridge, mode.to_sym, feedback[:action].to_sym, %i[adapter fennel]
+ )
+
+ lua = Logic::Cartridge::Tools.fetch_from_interface(
+ cartridge, mode.to_sym, feedback[:action].to_sym, %i[adapter lua]
+ )
+
+ clojure = Logic::Cartridge::Tools.fetch_from_interface(
+ cartridge, mode.to_sym, feedback[:action].to_sym, %i[adapter clojure]
+ )
+
+ { prefix:, suffix:, fennel:, lua:, clojure: }
+ end
+ end
+ end
+ end
+end
diff --git a/controllers/session.rb b/controllers/session.rb
index 4694911..378a11d 100644
--- a/controllers/session.rb
+++ b/controllers/session.rb
@@ -3,10 +3,14 @@
require 'babosa'
require 'fileutils'
+require 'rainbow'
require_relative '../logic/helpers/hash'
+require_relative '../logic/cartridge/safety'
require_relative '../logic/cartridge/streaming'
require_relative '../logic/cartridge/interaction'
+require_relative '../logic/cartridge/fetch'
+require_relative 'interfaces/tools'
require_relative '../components/storage'
require_relative '../components/adapter'
require_relative '../components/crypto'
@@ -14,6 +18,7 @@ require_relative '../components/crypto'
module NanoBot
module Controllers
STREAM_TIMEOUT_IN_SECONDS = 5
+ INFINITE_LOOP_PREVENTION = 10
class Session
attr_accessor :stream
@@ -41,9 +46,9 @@ module NanoBot
end
def load_state
- @state = Logic::Helpers::Hash.symbolize_keys(JSON.parse(
- Components::Crypto.decrypt(File.read(@state_path))
- ))
+ @state = Logic::Helpers::Hash.symbolize_keys(
+ JSON.parse(Components::Crypto.decrypt(File.read(@state_path)))
+ )
end
def store_state!
@@ -68,7 +73,7 @@ module NanoBot
mode: mode.to_s,
input: message,
message: Components::Adapter.apply(
- :input, Logic::Cartridge::Interaction.input(@cartridge, mode.to_sym, message)
+ Logic::Cartridge::Interaction.input(@cartridge, mode.to_sym, message), @cartridge
)
}
@@ -78,41 +83,88 @@ module NanoBot
end
def process(input, mode:)
+ interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
+
+ input[:interface] = interface
+ input[:tools] = @cartridge[:tools]
+
+ needs_another_round = true
+
+ rounds = 0
+
+ while needs_another_round
+ needs_another_round = process_interaction(input, mode:)
+ rounds += 1
+ raise StandardError, 'infinite loop prevention' if rounds > INFINITE_LOOP_PREVENTION
+ end
+ end
+
+ def process_interaction(input, mode:)
prefix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :prefix)
suffix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :suffix)
- interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
+ color = Logic::Cartridge::Fetch.cascate(
+ @cartridge, [[:interfaces, mode.to_sym, :output, :color], %i[interfaces output color]]
+ )
- streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym)
+ color = color.to_sym if color
- input[:interface] = interface
+ streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym)
updated_at = Time.now
ready = false
- @provider.evaluate(input) do |output, finished|
- updated_at = Time.now
-
- if finished
- event = Marshal.load(Marshal.dump(output))
- output = Logic::Cartridge::Interaction.output(
- @cartridge, mode.to_sym, output, streaming, finished
- )
+ needs_another_round = false
- output[:message] = Components::Adapter.apply(:output, output[:message])
+ @provider.evaluate(input, streaming, @cartridge) do |feedback|
+ needs_another_round = true if feedback[:needs_another_round]
- event[:mode] = mode.to_s
- event[:output] = "#{prefix}#{output[:message]}#{suffix}"
-
- @state[:history] << event
-
- self.print(output[:message]) unless streaming
+ updated_at = Time.now
- ready = true
- flush
- elsif streaming
- self.print(output[:message])
+ if feedback[:interaction] &&
+ feedback.dig(:interaction, :meta, :tool, :action) &&
+ feedback[:interaction][:meta][:tool][:action] == 'confirming'
+ Interfaces::Tool.confirming(self, @cartridge, mode, feedback[:interaction][:meta][:tool])
+ else
+
+ if feedback[:interaction] && feedback.dig(:interaction, :meta, :tool, :action)
+ Interfaces::Tool.dispatch_feedback(
+ self, @cartridge, mode, feedback[:interaction][:meta][:tool]
+ )
+ end
+
+ if feedback[:interaction]
+ event = Marshal.load(Marshal.dump(feedback[:interaction]))
+ event[:mode] = mode.to_s
+ event[:output] = nil
+
+ if feedback[:interaction][:who] == 'AI' && feedback[:interaction][:message]
+ event[:output] = feedback[:interaction][:message]
+ unless streaming
+ output = Logic::Cartridge::Interaction.output(
+ @cartridge, mode.to_sym, feedback[:interaction], streaming, feedback[:finished]
+ )
+ output[:message] = Components::Adapter.apply(output[:message], @cartridge)
+ event[:output] = (output[:message]).to_s
+ end
+ end
+
+ @state[:history] << event if feedback[:should_be_stored]
+
+ if event[:output] && ((!feedback[:finished] && streaming) || (!streaming && feedback[:finished]))
+ self.print(color ? Rainbow(event[:output]).send(color) : event[:output])
+ end
+
+ # The `print` function already outputs a prefix and a suffix, so
+ # we should add them afterwards to avoid printing them twice.
+ event[:output] = "#{prefix}#{event[:output]}#{suffix}"
+ end
+
+ if feedback[:finished]
+ flush
+ ready = true
+ end
end
end
@@ -122,6 +174,8 @@ module NanoBot
end
store_state! unless @stateless
+
+ needs_another_round
end
def flush