summaryrefslogtreecommitdiff
path: root/components/providers/tools.rb
diff options
context:
space:
mode:
Diffstat (limited to 'components/providers/tools.rb')
-rw-r--r--components/providers/tools.rb99
1 files changed, 99 insertions, 0 deletions
diff --git a/components/providers/tools.rb b/components/providers/tools.rb
new file mode 100644
index 0000000..122bc14
--- /dev/null
+++ b/components/providers/tools.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require_relative '../embedding'
+require_relative '../../logic/cartridge/safety'
+
+require 'concurrent'
+
+module NanoBot
+ module Components
+ module Providers
+ module Tools
+ def self.confirming(tool, feedback)
+ feedback.call(
+ { should_be_stored: false,
+ interaction: { who: 'AI', message: nil, meta: {
+ tool: { action: 'confirming', id: tool[:id], name: tool[:label], parameters: tool[:parameters] }
+ } } }
+ )
+ end
+
+ def self.apply(cartridge, function_cartridge, tools, feedback, tools_logic)
+ prepared_tools = tools_logic.prepare(function_cartridge, tools)
+
+ if Logic::Cartridge::Safety.confirmable?(cartridge)
+ prepared_tools.each { |tool| tool[:allowed] = confirming(tool, feedback) }
+ else
+ prepared_tools.each { |tool| tool[:allowed] = true }
+ end
+
+ futures = prepared_tools.map do |tool|
+ 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!
+
+ results.map do |applied_tool|
+ {
+ who: 'tool',
+ message: applied_tool[:output],
+ meta: { id: applied_tool[:id], name: applied_tool[:name] }
+ }
+ end
+ end
+
+ def self.process!(tool, feedback, _function_cartridge, cartridge)
+ feedback.call(
+ { should_be_stored: false,
+ interaction: { who: 'AI', message: nil, meta: {
+ tool: { action: 'executing', id: tool[:id], name: tool[:label], parameters: tool[:parameters] }
+ } } }
+ )
+
+ call = {
+ parameters: %w[parameters],
+ values: [tool[:parameters]],
+ safety: { sandboxed: Logic::Cartridge::Safety.sandboxed?(cartridge) }
+ }
+
+ if %i[fennel lua clojure].count { |key| !tool[:source][key].nil? } > 1
+ raise StandardError, 'conflicting tools'
+ end
+
+ if !tool[:source][:fennel].nil?
+ call[:source] = tool[:source][:fennel]
+ tool[:output] = Components::Embedding.fennel(**call)
+ elsif !tool[:source][:clojure].nil?
+ call[:source] = tool[:source][:clojure]
+ tool[:output] = Components::Embedding.clojure(**call)
+ elsif !tool[:source][:lua].nil?
+ call[:source] = tool[:source][:lua]
+ tool[:output] = Components::Embedding.lua(**call)
+ else
+ raise 'missing source code'
+ end
+
+ feedback.call(
+ { should_be_stored: false,
+ interaction: { who: 'AI', message: nil, meta: {
+ tool: {
+ action: 'responding', id: tool[:id], name: tool[:label],
+ parameters: tool[:parameters], output: tool[:output]
+ }
+ } } }
+ )
+
+ tool
+ end
+ end
+ end
+ end
+end