diff options
Diffstat (limited to 'controllers')
-rw-r--r-- | controllers/cartridges.rb | 2 | ||||
-rw-r--r-- | controllers/instance.rb | 8 | ||||
-rw-r--r-- | controllers/interfaces/repl.rb | 2 | ||||
-rw-r--r-- | controllers/interfaces/tools.rb | 90 | ||||
-rw-r--r-- | controllers/session.rb | 89 |
5 files changed, 162 insertions, 29 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/repl.rb b/controllers/interfaces/repl.rb index fd16ea6..d4191b1 100644 --- a/controllers/interfaces/repl.rb +++ b/controllers/interfaces/repl.rb @@ -37,6 +37,8 @@ module NanoBot [proc { prompt }, proc { 'MISSING INPUT' }] ) + Logic::Cartridge::Streaming.enabled?(cartridge, :repl) + Pry.commands.block_command(/(.*)/, 'handler') do |line| session.print(prefix) unless prefix.nil? session.evaluate_and_print(line, mode: 'repl') diff --git a/controllers/interfaces/tools.rb b/controllers/interfaces/tools.rb new file mode 100644 index 0000000..5105da1 --- /dev/null +++ b/controllers/interfaces/tools.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +require 'rainbow' + +require_relative '../../logic/cartridge/tools' +require_relative '../../components/embedding' + +module NanoBot + module Controllers + module Interfaces + module Tool + def self.adapt(feedback, adapter) + call = { + parameters: %w[id name parameters parameters-as-json output], + values: [ + feedback[:id], feedback[:name], feedback[:parameters], + feedback[:parameters].to_json, + feedback[:output] + ], + safety: false + } + + 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) + + 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) + else + message = "(#{feedback[:name]} #{feedback[:parameters].to_json})" + + message += " =>\n#{feedback[:output]}" if feedback[:action].to_sym == :response + 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..d20deb2 100644 --- a/controllers/session.rb +++ b/controllers/session.rb @@ -3,10 +3,13 @@ require 'babosa' require 'fileutils' +require 'rainbow' require_relative '../logic/helpers/hash' 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' @@ -41,9 +44,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! @@ -78,42 +81,78 @@ 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 + + # TODO: Improve infinite loop prevention. + needs_another_round = process_interaction(input, mode:) while needs_another_round + 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]) - - event[:mode] = mode.to_s - event[:output] = "#{prefix}#{output[:message]}#{suffix}" + @provider.evaluate(input) do |feedback| + updated_at = Time.now - @state[:history] << event + needs_another_round = true if feedback[:needs_another_round] - self.print(output[:message]) unless streaming + if feedback[:interaction] && feedback.dig(:interaction, :meta, :tool, :action) + Interfaces::Tool.dispatch_feedback(self, @cartridge, mode, feedback[:interaction][:meta][:tool]) + end - ready = true - flush - elsif streaming - self.print(output[:message]) + 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, output[:message]) + 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])) + # TODO: Color? + if color + self.print(Rainbow(event[:output]).send(color)) + else + self.print(event[:output]) + end + + flush if feedback[:finished] + end + + # `.print` already adds a prefix and suffix, so we add them after printing to avoid duplications. + event[:output] = "#{prefix}#{event[:output]}#{suffix}" end + + ready = true if feedback[:finished] end until ready @@ -122,6 +161,8 @@ module NanoBot end store_state! unless @stateless + + needs_another_round end def flush |