diff options
-rw-r--r-- | Gemfile.lock | 4 | ||||
-rw-r--r-- | components/storage.rb | 4 | ||||
-rw-r--r-- | controllers/instance.rb | 2 | ||||
-rw-r--r-- | controllers/interfaces/eval.rb | 21 | ||||
-rw-r--r-- | controllers/session.rb | 26 | ||||
-rw-r--r-- | logic/helpers/hash.rb | 4 | ||||
-rw-r--r-- | nano-bots.gemspec | 1 | ||||
-rw-r--r-- | spec/spec_helper.rb | 93 | ||||
-rw-r--r-- | static/cartridges/default.yml | 25 |
9 files changed, 57 insertions, 123 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 547a5b0..0acf361 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,6 +8,7 @@ PATH pry (~> 0.14.2) rainbow (~> 3.1, >= 3.1.1) ruby-openai (~> 4.0) + sweet-moon (~> 0.0.7) GEM remote: https://rubygems.org/ @@ -23,6 +24,7 @@ GEM faraday-multipart (1.0.4) multipart-post (~> 2) faraday-net_http (3.0.2) + ffi (1.15.5) json (2.6.3) method_source (1.0.0) multipart-post (2.3.0) @@ -73,6 +75,8 @@ GEM faraday-multipart (>= 1) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) + sweet-moon (0.0.7) + ffi (~> 1.15, >= 1.15.5) unicode-display_width (2.4.2) PLATFORMS diff --git a/components/storage.rb b/components/storage.rb index d46455f..bdf0efe 100644 --- a/components/storage.rb +++ b/components/storage.rb @@ -43,7 +43,7 @@ module NanoBot partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial))) - partial = path.sub(%r{^\.?/}, '') + partial = partial.sub(%r{^\.?/}, '') candidates << "#{directory}/#{partial}" candidates << "#{directory}/#{partial}.yml" @@ -54,7 +54,7 @@ module NanoBot partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial))) - partial = path.sub(%r{^\.?/}, '') + partial = partial.sub(%r{^\.?/}, '') candidates << "#{directory}/#{partial}" candidates << "#{directory}/#{partial}.yml" diff --git a/controllers/instance.rb b/controllers/instance.rb index 503b388..80ac9be 100644 --- a/controllers/instance.rb +++ b/controllers/instance.rb @@ -55,7 +55,7 @@ module NanoBot def load_cartridge!(path) elected_path = if path.strip == '-' - File.expand_path('../static/cartridges/default.yml', __dir__) + File.expand_path('../static/cartridges/baseline.yml', __dir__) else Components::Storage.cartridge_path(path) end diff --git a/controllers/interfaces/eval.rb b/controllers/interfaces/eval.rb index af519ab..851770b 100644 --- a/controllers/interfaces/eval.rb +++ b/controllers/interfaces/eval.rb @@ -4,34 +4,21 @@ require 'pry' require 'rainbow' require_relative '../../logic/helpers/hash' +require_relative '../../logic/cartridge/affixes' module NanoBot module Controllers module Interfaces module Eval def self.evaluate(input, cartridge, session) - prefix = build_prefix(cartridge) - postfix = build_postfix(cartridge) + prefix = Logic::Cartridge::Affixes.get(cartridge, :eval, :output, :prefix) + suffix = Logic::Cartridge::Affixes.get(cartridge, :eval, :output, :suffix) session.print(prefix) unless prefix.nil? session.evaluate_and_print(input, mode: 'eval') - session.print(postfix) unless postfix.nil? - end - - def self.build_prefix(cartridge) - eval_interface = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces eval]) - return nil if eval_interface.nil? - - eval_interface[:prefix] - end - - def self.build_postfix(cartridge) - eval_interface = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces eval]) - return "\n" if eval_interface.nil? || !eval_interface.key?(:postfix) # default - - eval_interface[:postfix] + session.print(suffix) unless suffix.nil? end end end diff --git a/controllers/session.rb b/controllers/session.rb index 1eb600d..d61fdd5 100644 --- a/controllers/session.rb +++ b/controllers/session.rb @@ -5,7 +5,10 @@ require 'babosa' require 'fileutils' require_relative '../logic/helpers/hash' +require_relative '../logic/cartridge/streaming' +require_relative '../logic/cartridge/interaction' require_relative '../components/storage' +require_relative '../components/adapter' module NanoBot module Controllers @@ -56,24 +59,22 @@ module NanoBot def evaluate_and_print(message, mode:) behavior = Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors interaction]) || {} - @state[:history] << ({ who: 'user', message: }) + @state[:history] << { + who: 'user', + message: Components::Adapter.apply( + :input, Logic::Cartridge::Interaction.input(@cartridge, mode.to_sym, message) + ) + } input = { behavior:, history: @state[:history] } process(input, mode:) end - def streaming(interface) - provider = @provider.settings.key?(:stream) ? @provider.settings[:stream] : true - interface = interface.key?(:stream) ? interface[:stream] : true - - provider && interface - end - def process(input, mode:) interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {} - streaming = streaming(interface) + streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym) input[:interface] = interface @@ -81,7 +82,14 @@ module NanoBot ready = false @provider.evaluate(input) do |output, finished| + output = Logic::Cartridge::Interaction.output( + @cartridge, mode.to_sym, output, streaming, finished + ) + + output[:message] = Components::Adapter.apply(:output, output[:message]) + updated_at = Time.now + if finished @state[:history] << output self.print(output[:message]) unless streaming diff --git a/logic/helpers/hash.rb b/logic/helpers/hash.rb index 52bd8d4..90432b5 100644 --- a/logic/helpers/hash.rb +++ b/logic/helpers/hash.rb @@ -23,6 +23,10 @@ module NanoBot return nil unless node path.each do |key| + unless node.is_a?(::Hash) + node = nil + break + end node = node[key] break if node.nil? end diff --git a/nano-bots.gemspec b/nano-bots.gemspec index 0a3d9a6..b717371 100644 --- a/nano-bots.gemspec +++ b/nano-bots.gemspec @@ -37,6 +37,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'pry', '~> 0.14.2' spec.add_dependency 'rainbow', '~> 3.1', '>= 3.1.1' spec.add_dependency 'ruby-openai', '~> 4.0' + spec.add_dependency 'sweet-moon', '~> 0.0.7' spec.metadata['rubygems_mfa_required'] = 'true' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4f8c8d9..ad9038d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,98 +1,23 @@ # frozen_string_literal: true -# This file was generated by the `rspec --init` command. Conventionally, all -# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. -# The generated `.rspec` file contains `--require spec_helper` which will cause -# this file to always be loaded, without a need to explicitly require it in any -# files. -# -# Given that it is always loaded, you are encouraged to keep this file as -# light-weight as possible. Requiring heavyweight dependencies from this file -# will add to the boot time of your test suite on EVERY test run, even for an -# individual file that may not need all of that loaded. Instead, consider making -# a separate helper file that requires the additional dependencies and performs -# the additional setup, and require it from the spec files that actually need -# it. -# -# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +require 'yaml' + +require_relative '../logic/helpers/hash' + RSpec.configure do |config| - # rspec-expectations config goes here. You can use an alternate - # assertion/expectation library such as wrong or the stdlib/minitest - # assertions if you prefer. config.expect_with :rspec do |expectations| - # This option will default to `true` in RSpec 4. It makes the `description` - # and `failure_message` of custom matchers include text for helper methods - # defined using `chain`, e.g.: - # be_bigger_than(2).and_smaller_than(4).description - # # => "be bigger than 2 and smaller than 4" - # ...rather than: - # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end - # rspec-mocks config goes here. You can use an alternate test double - # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| - # Prevents you from mocking or stubbing a method that does not exist on - # a real object. This is generally recommended, and will default to - # `true` in RSpec 4. mocks.verify_partial_doubles = true end - # This option will default to `:apply_to_host_groups` in RSpec 4 (and will - # have no way to turn it off -- the option exists only for backwards - # compatibility in RSpec 3). It causes shared context metadata to be - # inherited by the metadata hash of host groups and examples, rather than - # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups +end + +def load_cartridge(path) + cartridge = YAML.safe_load(File.read("spec/data/cartridges/#{path}"), permitted_classes: [Symbol]) - # The settings below are suggested to provide a good initial experience - # with RSpec, but feel free to customize to your heart's content. - # # This allows you to limit a spec run to individual examples or groups - # # you care about by tagging them with `:focus` metadata. When nothing - # # is tagged with `:focus`, all examples get run. RSpec also provides - # # aliases for `it`, `describe`, and `context` that include `:focus` - # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - # config.filter_run_when_matching :focus - # - # # Allows RSpec to persist some state between runs in order to support - # # the `--only-failures` and `--next-failure` CLI options. We recommend - # # you configure your source control system to ignore this file. - # config.example_status_persistence_file_path = "spec/examples.txt" - # - # # Limits the available syntax to the non-monkey patched syntax that is - # # recommended. For more details, see: - # # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ - # config.disable_monkey_patching! - # - # # This setting enables warnings. It's recommended, but in some cases may - # # be too noisy due to issues in dependencies. - # config.warnings = true - # - # # Many RSpec users commonly either run the entire suite or an individual - # # file, and it's useful to allow more verbose output when running an - # # individual spec file. - # if config.files_to_run.one? - # # Use the documentation formatter for detailed output, - # # unless a formatter has already been configured - # # (e.g. via a command-line flag). - # config.default_formatter = "doc" - # end - # - # # Print the 10 slowest examples and example groups at the - # # end of the spec run, to help surface which specs are running - # # particularly slow. - # config.profile_examples = 10 - # - # # Run specs in random order to surface order dependencies. If you find an - # # order dependency and want to debug it, you can fix the order by providing - # # the seed, which is printed after each run. - # # --seed 1234 - # config.order = :random - # - # # Seed global randomization in this process using the `--seed` CLI option. - # # Setting this allows you to use `--seed` to deterministically reproduce - # # test failures related to randomization by passing the same `--seed` value - # # as the one that triggered the failure. - # Kernel.srand config.seed + NanoBot::Logic::Helpers::Hash.symbolize_keys(cartridge) end diff --git a/static/cartridges/default.yml b/static/cartridges/default.yml index b5eed62..df609ed 100644 --- a/static/cartridges/default.yml +++ b/static/cartridges/default.yml @@ -1,14 +1,19 @@ --- -meta: - name: Unknown - author: Nobody - version: 0.0.0 +interfaces: + repl: + output: + suffix: "\n" + prefix: "\n" + stream: true + prompt: + - text: '🤖' + - text: '> ' + color: blue + eval: + output: + stream: true + suffix: "\n" provider: - name: openai settings: - model: gpt-3.5-turbo - credentials: - address: ENV/OPENAI_API_ADDRESS - access-token: ENV/OPENAI_API_ACCESS_TOKEN - user-identifier: ENV/OPENAI_API_USER_IDENTIFIER + stream: true |