diff options
author | icebaker <113217272+icebaker@users.noreply.github.com> | 2023-11-29 07:53:19 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-29 07:53:19 -0300 |
commit | 9f79a161905f5af8e331930cc77c7be10703596f (patch) | |
tree | b2ab75665e42de79f22cca82bf03cfc49759f485 /components/providers/openai.rb | |
parent | e1ab6853262b83f483060961f17bf895989a19c0 (diff) | |
parent | 154aa68caf50a18af5c0dff1d368fc639314e0ba (diff) |
Merge pull request #5 from icebaker/ib-tools
Adding support for Spec 1.0.0: Tools (Functions)
Diffstat (limited to 'components/providers/openai.rb')
-rw-r--r-- | components/providers/openai.rb | 123 |
1 files changed, 103 insertions, 20 deletions
diff --git a/components/providers/openai.rb b/components/providers/openai.rb index ce6fb33..87f2bc5 100644 --- a/components/providers/openai.rb +++ b/components/providers/openai.rb @@ -2,9 +2,14 @@ require 'openai' -require_relative './base' +require_relative 'base' require_relative '../crypto' +require_relative '../../logic/providers/openai/tools' +require_relative '../../controllers/interfaces/tools' + +require_relative 'openai/tools' + module NanoBot module Components module Providers @@ -32,17 +37,16 @@ module NanoBot @client = ::OpenAI::Client.new(uri_base:, access_token: @credentials[:'access-token']) end - def stream(input) - provider = @settings.key?(:stream) ? @settings[:stream] : true - interface = input[:interface].key?(:stream) ? input[:interface][:stream] : true - - provider && interface - end - - def evaluate(input, &block) + def evaluate(input, streaming, cartridge, &feedback) messages = input[:history].map do |event| - { role: event[:who] == 'user' ? 'user' : 'assistant', - content: event[:message] } + if event[:message].nil? && event[:meta] && event[:meta][:tool_calls] + { role: 'assistant', content: nil, tool_calls: event[:meta][:tool_calls] } + elsif event[:who] == 'tool' + { role: event[:who], content: event[:message].to_s, + tool_call_id: event[:meta][:id], name: event[:meta][:name] } + else + { role: event[:who] == 'user' ? 'user' : 'assistant', content: event[:message] } + end end %i[instruction backdrop directive].each do |key| @@ -62,26 +66,105 @@ module NanoBot payload.delete(:logit_bias) if payload.key?(:logit_bias) && payload[:logit_bias].nil? - if stream(input) + payload[:tools] = input[:tools].map { |raw| NanoBot::Logic::OpenAI::Tools.adapt(raw) } if input[:tools] + + if streaming content = '' + tools = [] payload[:stream] = proc do |chunk, _bytesize| - partial = chunk.dig('choices', 0, 'delta', 'content') - if partial - content += partial - block.call({ who: 'AI', message: partial }, false) + partial_content = chunk.dig('choices', 0, 'delta', 'content') + partial_tools = chunk.dig('choices', 0, 'delta', 'tool_calls') + + if partial_tools + partial_tools.each do |partial_tool| + tools[partial_tool['index']] = {} if tools[partial_tool['index']].nil? + + partial_tool.keys.reject { |key| ['index'].include?(key) }.each do |key| + target = tools[partial_tool['index']] + + if partial_tool[key].is_a?(Hash) + target[key] = {} if target[key].nil? + partial_tool[key].each_key do |sub_key| + target[key][sub_key] = '' if target[key][sub_key].nil? + + target[key][sub_key] += partial_tool[key][sub_key] + end + else + target[key] = '' if target[key].nil? + + target[key] += partial_tool[key] + end + end + end end - block.call({ who: 'AI', message: content }, true) if chunk.dig('choices', 0, 'finish_reason') + if partial_content + content += partial_content + feedback.call( + { should_be_stored: false, + interaction: { who: 'AI', message: partial_content } } + ) + end + + if chunk.dig('choices', 0, 'finish_reason') + if tools&.size&.positive? + feedback.call( + { should_be_stored: true, + needs_another_round: true, + interaction: { who: 'AI', message: nil, meta: { tool_calls: tools } } } + ) + Tools.apply(cartridge, input[:tools], tools, feedback).each do |interaction| + feedback.call({ should_be_stored: true, needs_another_round: true, interaction: }) + end + end + + feedback.call( + { should_be_stored: !(content.nil? || content == ''), + interaction: content.nil? || content == '' ? nil : { who: 'AI', message: content }, + finished: true } + ) + end end - @client.chat(parameters: payload) + begin + @client.chat(parameters: payload) + rescue StandardError => e + raise e.class, e.response[:body] if e.response && e.response[:body] + + raise e + end else - result = @client.chat(parameters: payload) + begin + result = @client.chat(parameters: payload) + rescue StandardError => e + raise e.class, e.response[:body] if e.response && e.response[:body] + + raise e + end raise StandardError, result['error'] if result['error'] - block.call({ who: 'AI', message: result.dig('choices', 0, 'message', 'content') }, true) + tools = result.dig('choices', 0, 'message', 'tool_calls') + + if tools&.size&.positive? + feedback.call( + { should_be_stored: true, + needs_another_round: true, + interaction: { who: 'AI', message: nil, meta: { tool_calls: tools } } } + ) + Tools.apply(cartridge, input[:tools], tools, feedback).each do |interaction| + feedback.call({ should_be_stored: true, needs_another_round: true, interaction: }) + end + end + + content = result.dig('choices', 0, 'message', 'content') + + feedback.call( + { should_be_stored: !(content.nil? || content == ''), + interaction: content.nil? || content == '' ? nil : { who: 'AI', message: content }, + finished: true } + ) end end |