diff options
-rw-r--r-- | README.md | 66 | ||||
-rwxr-xr-x | bin/nb (renamed from bin/rnb) | 0 | ||||
-rw-r--r-- | components/providers/openai.rb | 9 | ||||
-rw-r--r-- | components/storage.rb | 5 | ||||
-rw-r--r-- | controllers/instance.rb | 3 | ||||
-rw-r--r-- | controllers/interfaces/cli.rb | 16 | ||||
-rw-r--r-- | controllers/interfaces/eval.rb | 37 | ||||
-rw-r--r-- | controllers/interfaces/repl.rb | 63 | ||||
-rw-r--r-- | controllers/session.rb | 16 | ||||
-rw-r--r-- | nano-bots.gemspec | 2 |
10 files changed, 140 insertions, 77 deletions
@@ -81,32 +81,32 @@ docker compose run nano-bots Start playing: ```sh -rnb cartridges/assistant.yml - repl +nb cartridges/assistant.yml - repl ``` ## Usage ### Command Line -After installing the gem, the `rnb` binary command will be available for your project or system. +After installing the gem, the `nb` binary command will be available for your project or system. Examples of usage: ```bash -rnb to-en-us-translator.yml - eval "Salut, comment ça va?" +nb to-en-us-translator.yml - eval "Salut, comment ça va?" # => Hello, how are you doing? -rnb midjourney.yml - eval "happy and friendly cyberpunk robot" -# => The robot exploring a bustling city, surrounded by neon lights -# and high-rise buildings. The prompt should include colorful -# lighting and a sense of excitement in the facial expression. +nb midjourney.yml - eval "happy cyberpunk robot" +# => A cheerful and fun-loving robot is dancing wildly amidst a +# futuristic and lively cityscape. Holographic advertisements +# and vibrant neon colors can be seen in the background. -rnb lisp.yml - eval "(+ 1 2)" +nb lisp.yml - eval "(+ 1 2)" # => 3 cat article.txt | - rnb to-en-us-translator.yml - eval | - rnb summarizer.yml - eval + nb to-en-us-translator.yml - eval | + nb summarizer.yml - eval # -> LLM stands for Large Language Model, which refers to an # artificial intelligence algorithm capable of processing # and understanding vast amounts of natural language data, @@ -115,23 +115,34 @@ cat article.txt | ``` ```bash -rnb assistant.yml - repl +nb assistant.yml - repl ``` -All of the commands above are stateless. If you want to preserve the history of your interactions, replace the `-` with a state key. You can use a simple key, such as your username, or a randomly generated one: +```text +🤖> Hi, how are you doing? -```ruby -require 'securerandom' +As an AI language model, I do not experience emotions but I am functioning +well. How can I assist you? -SecureRandom.hex # => 6ea6c43c42a1c076b1e3c36fa349ac2c +🤖> | ``` +All of the commands above are stateless. If you want to preserve the history of your interactions, replace the `-` with a state key: + ```bash -rnb assistant.yml your-user eval "Salut, comment ça va?" -rnb assistant.yml your-user repl +nb assistant.yml your-user eval "Salut, comment ça va?" +nb assistant.yml your-user repl + +nb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c eval "Salut, comment ça va?" +nb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c repl +``` -rnb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c eval "Salut, comment ça va?" -rnb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c repl +You can use a simple key, such as your username, or a randomly generated one: + +```ruby +require 'securerandom' + +SecureRandom.hex # => 6ea6c43c42a1c076b1e3c36fa349ac2c ``` ### Library @@ -139,13 +150,13 @@ rnb assistant.yml 6ea6c43c42a1c076b1e3c36fa349ac2c repl To use it as a library: ```ruby -require 'nano-bots/cli' # Equivalent to the `rnb` command. +require 'nano-bots/cli' # Equivalent to the `nb` command. ``` ```ruby require 'nano-bots' -NanoBot.cli # Equivalent to the `rnb` command. +NanoBot.cli # Equivalent to the `nb` command. NanoBot.repl(cartridge: 'cartridge.yml') # Starts a new REPL. @@ -166,20 +177,15 @@ Here's what a Nano Bot Cartridge looks like: ```yaml --- -name: Assistant -version: 0.0.1 +meta: + name: Nano Bot Name + author: Your Name + version: 0.0.1 behaviors: interaction: directive: You are a helpful assistant. -interfaces: - repl: - prompt: - - text: '🤖' - - text: '> ' - color: blue - provider: name: openai settings: diff --git a/components/providers/openai.rb b/components/providers/openai.rb index e163573..c0a6639 100644 --- a/components/providers/openai.rb +++ b/components/providers/openai.rb @@ -24,6 +24,13 @@ module NanoBot ) 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) messages = input[:history].map do |event| { role: event[:who] == 'user' ? 'user' : 'assistant', @@ -51,7 +58,7 @@ module NanoBot payload.delete(:logit_bias) if payload.key?(:logit_bias) && payload[:logit_bias].nil? - if @settings[:stream] && input[:interface][:stream] + if stream(input) content = '' payload[:stream] = proc do |chunk, _bytesize| diff --git a/components/storage.rb b/components/storage.rb index 5f51d06..3d3f58d 100644 --- a/components/storage.rb +++ b/components/storage.rb @@ -17,8 +17,9 @@ module NanoBot path = "#{user_home!.sub(%r{/$}, '')}/.local/state/nano-bots" if path.nil? - path = "#{path.sub(%r{/$}, '')}/nano-bots-rb/#{cartridge[:name].to_slug.normalize}" - path = "#{path}/#{cartridge[:version].to_slug.normalize}/#{key.to_slug.normalize}" + path = "#{path.sub(%r{/$}, '')}/ruby-nano-bots/#{cartridge[:meta][:author].to_slug.normalize}" + path = "#{path}/#{cartridge[:meta][:name].to_slug.normalize}" + path = "#{path}/#{cartridge[:meta][:version].to_s.gsub('.', '-').to_slug.normalize}/#{key.to_slug.normalize}" path = "#{path}/state.json" FileUtils.mkdir_p(File.dirname(path)) diff --git a/controllers/instance.rb b/controllers/instance.rb index e8037e2..f3cbd74 100644 --- a/controllers/instance.rb +++ b/controllers/instance.rb @@ -6,6 +6,7 @@ require_relative '../logic/helpers/hash' require_relative '../components/provider' require_relative '../components/storage' require_relative './interfaces/repl' +require_relative './interfaces/eval' require_relative './session' module NanoBot @@ -24,7 +25,7 @@ module NanoBot end def eval(input) - @session.evaluate_and_print(input, mode: 'eval') + Interfaces::Eval.evaluate(input, @cartridge, @session) end def repl diff --git a/controllers/interfaces/cli.rb b/controllers/interfaces/cli.rb index 550361f..3574a80 100644 --- a/controllers/interfaces/cli.rb +++ b/controllers/interfaces/cli.rb @@ -13,14 +13,14 @@ module NanoBot puts NanoBot::GEM[:version] exit when 'help', '', nil - puts "Ruby Nano Bots #{NanoBot::GEM[:version]}" - puts ' rnb cartridge.yml - eval "Hello"' - puts ' rnb cartridge.yml - repl' - puts ' rnb cartridge.yml - debug' - puts ' rnb cartridge.yml STATE-KEY eval "Hello"' - puts ' rnb cartridge.yml STATE-KEY repl' - puts ' rnb cartridge.yml STATE-KEY debug' - puts ' rnb version' + puts "Nano Bots #{NanoBot::GEM[:version]}" + puts ' nb cartridge.yml - eval "Hello"' + puts ' nb cartridge.yml - repl' + puts ' nb cartridge.yml - debug' + puts ' nb cartridge.yml STATE-KEY eval "Hello"' + puts ' nb cartridge.yml STATE-KEY repl' + puts ' nb cartridge.yml STATE-KEY debug' + puts ' nb version' exit end diff --git a/controllers/interfaces/eval.rb b/controllers/interfaces/eval.rb new file mode 100644 index 0000000..6cb12b4 --- /dev/null +++ b/controllers/interfaces/eval.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'pry' +require 'rainbow' + +require_relative '../../logic/helpers/hash' + +module NanoBot + module Controllers + module Interfaces + module Eval + def self.evaluate(input, cartridge, session) + prefix = build_prefix(cartridge) + postfix = build_postfix(cartridge) + + 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] + end + end + end + end +end diff --git a/controllers/interfaces/repl.rb b/controllers/interfaces/repl.rb index 7b53eb2..5477c33 100644 --- a/controllers/interfaces/repl.rb +++ b/controllers/interfaces/repl.rb @@ -10,20 +10,17 @@ module NanoBot module Interfaces module REPL def self.start(cartridge, session) - if Logic::Helpers::Hash.fetch( - cartridge, %i[interfaces repl prefix] - ) - session.print(Logic::Helpers::Hash.fetch(cartridge, - %i[interfaces repl prefix])) - end - - session.boot(mode: 'repl') + prefix = build_prefix(cartridge) + postfix = build_postfix(cartridge) - session.print(Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl postfix]) || "\n") - - session.flush + if Logic::Helpers::Hash.fetch(cartridge, %i[behaviors boot instruction]) + session.print(prefix) unless prefix.nil? + session.boot(mode: 'repl') + session.print(postfix) unless postfix.nil? + session.print("\n") + end - prompt = build_prompt(cartridge[:interfaces][:repl][:prompt]) + prompt = build_prompt(Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl prompt])) Pry.config.prompt = Pry::Prompt.new( 'REPL', @@ -32,31 +29,43 @@ module NanoBot ) Pry.commands.block_command(/(.*)/, 'handler') do |line| - if Logic::Helpers::Hash.fetch( - cartridge, %i[interfaces repl prefix] - ) - session.print(Logic::Helpers::Hash.fetch( - cartridge, %i[interfaces repl prefix] - )) - end - + session.print(postfix) unless postfix.nil? session.evaluate_and_print(line, mode: 'repl') - session.print(Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl postfix]) || "\n") + session.print(postfix) unless postfix.nil? + session.print("\n") session.flush end Pry.start end + def self.build_prefix(cartridge) + repl = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl]) + return "\n" if repl.nil? || !repl.key?(:prefix) # default + repl[:prefix] + end + + def self.build_postfix(cartridge) + repl = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl]) + return "\n" if repl.nil? || !repl.key?(:postfix) # default + repl[:postfix] + end + def self.build_prompt(prompt) result = '' - prompt.each do |partial| - result += if partial[:color] - Rainbow(partial[:text]).send(partial[:color]) - else - partial[:text] - end + if prompt.is_a?(Array) + prompt.each do |partial| + result += if partial[:color] + Rainbow(partial[:text]).send(partial[:color]) + else + partial[:text] + end + end + elsif prompt.is_a?(String) + result = prompt + else + result = '🤖' + Rainbow('> ').blue end result diff --git a/controllers/session.rb b/controllers/session.rb index 9277a84..ff3b8ad 100644 --- a/controllers/session.rb +++ b/controllers/session.rb @@ -67,13 +67,18 @@ module NanoBot process(input, mode:) end - def process(input, mode:) - streaming = @provider.settings[:stream] && Logic::Helpers::Hash.fetch( - @cartridge, [:interfaces, mode.to_sym, :stream] - ) + def stream(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 = stream(interface) + input[:interface] = interface updated_at = Time.now @@ -84,9 +89,6 @@ module NanoBot if finished @state[:history] << output self.print(output[:message]) unless streaming - unless Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym, :postfix]).nil? - self.print(Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym, :postfix])) - end ready = true flush elsif streaming diff --git a/nano-bots.gemspec b/nano-bots.gemspec index 88a1270..0a3d9a6 100644 --- a/nano-bots.gemspec +++ b/nano-bots.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['ports/dsl'] - spec.executables = ['rnb'] + spec.executables = ['nb'] spec.add_dependency 'babosa', '~> 2.0' spec.add_dependency 'dotenv', '~> 2.8', '>= 2.8.1' |