summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoricebaker <icebaker@proton.me>2023-11-18 21:45:16 -0300
committericebaker <icebaker@proton.me>2023-11-18 21:45:16 -0300
commitc470d63b169058d81f44569a5f1c4c1fb222279f (patch)
tree8d2f520d4eb73e65a44e3815f36e38ff09943d52
parent89962f27a75183947fc44cd051a1061ce157221d (diff)
adding support for confirm
-rw-r--r--components/embedding.rb2
-rw-r--r--components/providers/openai/tools.rb26
-rw-r--r--controllers/interfaces/tools.rb13
-rw-r--r--controllers/session.rb75
-rw-r--r--logic/cartridge/safety.rb21
5 files changed, 101 insertions, 36 deletions
diff --git a/components/embedding.rb b/components/embedding.rb
index 46d0fba..f08ff3d 100644
--- a/components/embedding.rb
+++ b/components/embedding.rb
@@ -33,7 +33,7 @@ module NanoBot
path = "#{File.expand_path('../static/fennel', __dir__)}/?.lua"
state = SweetMoon::State.new(package_path: path).fennel
- # TODO: global is deprecated...
+ # TODO: `global` is deprecated.
state.fennel.eval(
"(global embedded (fn [#{parameters.join(' ')}] #{source}))", 1,
safety[:sandboxed] ? { allowedGlobals: %w[math string table] } : nil
diff --git a/components/providers/openai/tools.rb b/components/providers/openai/tools.rb
index 50eead9..10c2709 100644
--- a/components/providers/openai/tools.rb
+++ b/components/providers/openai/tools.rb
@@ -10,12 +10,34 @@ module NanoBot
module Providers
class OpenAI < Base
module Tools
+ def self.confirm(tool, feedback)
+ feedback.call(
+ { should_be_stored: false,
+ interaction: { who: 'AI', message: nil, meta: {
+ tool: { action: 'confirm', id: tool[:id], name: tool[:name], parameters: tool[:parameters] }
+ } } }
+ )
+ end
+
def self.apply(cartridge, function_cartridge, tools, feedback)
prepared_tools = NanoBot::Logic::OpenAI::Tools.prepare(function_cartridge, tools)
- # TODO: Confirm before starting futures.
+ if Logic::Cartridge::Safety.confirmable?(cartridge)
+ prepared_tools.each { |tool| tool[:allowed] = confirm(tool, feedback) }
+ else
+ prepared_tools.each { |tool| tool[:allowed] = true }
+ end
+
futures = prepared_tools.map do |tool|
- Concurrent::Promises.future { process!(tool, feedback, function_cartridge, cartridge) }
+ Concurrent::Promises.future do
+ if tool[:allowed]
+ process!(tool, feedback, function_cartridge, cartridge)
+ else
+ tool[:output] =
+ "We asked the user you're chatting with for permission, but the user did not allow you to run this tool or function."
+ tool
+ end
+ end
end
results = Concurrent::Promises.zip(*futures).value!
diff --git a/controllers/interfaces/tools.rb b/controllers/interfaces/tools.rb
index 1136600..d32afed 100644
--- a/controllers/interfaces/tools.rb
+++ b/controllers/interfaces/tools.rb
@@ -10,6 +10,17 @@ module NanoBot
module Controllers
module Interfaces
module Tool
+ def self.confirm(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],
@@ -41,6 +52,8 @@ module NanoBot
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 == :confirm
+
return unless enabled
color = Logic::Cartridge::Tools.fetch_from_interface(
diff --git a/controllers/session.rb b/controllers/session.rb
index 546a891..b3cc7ef 100644
--- a/controllers/session.rb
+++ b/controllers/session.rb
@@ -6,6 +6,7 @@ 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'
@@ -102,10 +103,9 @@ module NanoBot
prefix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :prefix)
suffix = Logic::Cartridge::Affixes.get(@cartridge, mode.to_sym, :output, :suffix)
- color = Logic::Cartridge::Fetch.cascate(@cartridge, [
- [:interfaces, mode.to_sym, :output, :color],
- %i[interfaces output color]
- ])
+ color = Logic::Cartridge::Fetch.cascate(
+ @cartridge, [[:interfaces, mode.to_sym, :output, :color], %i[interfaces output color]]
+ )
color = color.to_sym if color
@@ -118,44 +118,53 @@ module NanoBot
needs_another_round = false
@provider.evaluate(input, @cartridge) do |feedback|
+ needs_another_round = true if feedback[:needs_another_round]
+
updated_at = Time.now
- needs_another_round = true if feedback[:needs_another_round]
+ if feedback[:interaction] &&
+ feedback.dig(:interaction, :meta, :tool, :action) &&
+ feedback[:interaction][:meta][:tool][:action] == 'confirm'
+ Interfaces::Tool.confirm(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] && 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
+ 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
- end
- @state[:history] << event if feedback[:should_be_stored]
+ @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
+ 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
+ # 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
+ if feedback[:finished]
+ flush
+ ready = true
+ end
end
end
diff --git a/logic/cartridge/safety.rb b/logic/cartridge/safety.rb
index 84b39d7..6414e51 100644
--- a/logic/cartridge/safety.rb
+++ b/logic/cartridge/safety.rb
@@ -6,6 +6,27 @@ module NanoBot
module Logic
module Cartridge
module Safety
+ def self.default_answer(cartridge)
+ default = Fetch.cascate(cartridge, [%i[interfaces tools confirm default]])
+ return [] if default.nil?
+
+ default
+ end
+
+ def self.yeses(cartridge)
+ yeses_values = Fetch.cascate(cartridge, [%i[interfaces tools confirm yeses]])
+ return [] if yeses_values.nil?
+
+ yeses_values
+ end
+
+ def self.confirmable?(cartridge)
+ confirmable = Fetch.cascate(cartridge, [%i[safety tools confirmable]])
+ return true if confirmable.nil?
+
+ confirmable
+ end
+
def self.sandboxed?(cartridge)
sandboxed = Fetch.cascate(cartridge, [%i[safety functions sandboxed]])
return true if sandboxed.nil?