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