controllers/session.rb in nano-bots-0.1.1 vs controllers/session.rb in nano-bots-1.0.0

- old
+ new

@@ -1,21 +1,26 @@ # frozen_string_literal: true 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' module NanoBot module Controllers STREAM_TIMEOUT_IN_SECONDS = 5 + INFINITE_LOOP_PREVENTION = 10 class Session attr_accessor :stream def initialize(provider:, cartridge:, state: nil, stream: $stdout, environment: {}) @@ -39,13 +44,13 @@ def state { state: { path: @state_path, content: @state } } 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! File.write(@state_path, Components::Crypto.encrypt(JSON.generate(@state))) end @@ -66,63 +71,112 @@ @state[:history] << { who: 'user', 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 ) } input = { behavior:, history: @state[:history] } process(input, mode:) 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]] + ) + color = color.to_sym if color + streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym) - input[:interface] = interface - updated_at = Time.now ready = false - @provider.evaluate(input) do |output, finished| + + needs_another_round = false + + @provider.evaluate(input, streaming, @cartridge) do |feedback| + needs_another_round = true if feedback[:needs_another_round] + updated_at = Time.now - if finished - event = Marshal.load(Marshal.dump(output)) + 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 - output = Logic::Cartridge::Interaction.output( - @cartridge, mode.to_sym, output, streaming, finished - ) + if feedback[:interaction] && feedback.dig(:interaction, :meta, :tool, :action) + Interfaces::Tool.dispatch_feedback( + self, @cartridge, mode, feedback[:interaction][:meta][:tool] + ) + end - output[:message] = Components::Adapter.apply(:output, output[:message]) + if feedback[:interaction] + event = Marshal.load(Marshal.dump(feedback[:interaction])) + event[:mode] = mode.to_s + event[:output] = nil - event[:mode] = mode.to_s - event[:output] = "#{prefix}#{output[:message]}#{suffix}" + 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 + @state[:history] << event if feedback[:should_be_stored] - self.print(output[:message]) unless streaming + if event[:output] && ((!feedback[:finished] && streaming) || (!streaming && feedback[:finished])) + self.print(color ? Rainbow(event[:output]).send(color) : event[:output]) + end - ready = true - flush - elsif streaming - self.print(output[:message]) + # 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 until ready seconds = (Time.now - updated_at).to_i raise StandardError, 'The stream has become unresponsive.' if seconds >= STREAM_TIMEOUT_IN_SECONDS end store_state! unless @stateless + + needs_another_round end def flush @stream.flush end