diff options
Diffstat (limited to 'spec')
-rw-r--r-- | spec/components/storage_spec.rb | 53 | ||||
-rw-r--r-- | spec/data/cartridges/block.md | 7 | ||||
-rw-r--r-- | spec/data/cartridges/markdown.md | 37 | ||||
-rw-r--r-- | spec/data/cartridges/meta.md | 25 | ||||
-rw-r--r-- | spec/data/cartridges/models/ollama/phi-2.yml (renamed from spec/data/cartridges/models/ollama/llama2.yml) | 4 | ||||
-rw-r--r-- | spec/data/cartridges/tools.md | 76 | ||||
-rw-r--r-- | spec/logic/cartridge/parser_spec.rb | 119 | ||||
-rw-r--r-- | spec/logic/helpers/hash_spec.rb | 17 |
8 files changed, 336 insertions, 2 deletions
diff --git a/spec/components/storage_spec.rb b/spec/components/storage_spec.rb new file mode 100644 index 0000000..99131dd --- /dev/null +++ b/spec/components/storage_spec.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require_relative '../../components/storage' + +RSpec.describe NanoBot::Components::Storage do + it 'symbolizes keys' do + expect( + described_class.cartridges_path( + components: { home: '/home/aqua', ENV: {}, directory?: ->(_) { true } } + ) + ).to eq('/home/aqua/.local/share/nano-bots/cartridges') + + expect( + described_class.cartridges_path( + components: { + home: '/home/aqua', + ENV: { 'NANO_BOTS_CARTRIDGES_DIRECTORY' => '/home/aqua/my-cartridges' }, + directory?: ->(_) { true } + } + ) + ).to eq('/home/aqua/my-cartridges') + + expect( + described_class.cartridges_path( + components: { + home: '/home/aqua', + ENV: { + 'NANO_BOTS_CARTRIDGES_DIRECTORY' => '/home/aqua/my-cartridges', + 'NANO_BOTS_CARTRIDGES_PATH' => '/home/aqua/lime/my-cartridges' + }, + directory?: ->(_) { true } + } + ) + ).to eq('/home/aqua/lime/my-cartridges:/home/aqua/my-cartridges') + + expect( + described_class.cartridges_path( + components: { + home: '/home/aqua', + ENV: { + 'NANO_BOTS_CARTRIDGES_DIRECTORY' => '/home/aqua/my-cartridges', + 'NANO_BOTS_CARTRIDGES_PATH' => '/home/aqua/lime/my-cartridges:/home/aqua/ivory/my-cartridges' + }, + directory?: lambda do |path| + { '/home/aqua/my-cartridges' => true, + '/home/aqua/lime/my-cartridge' => false, + '/home/aqua/ivory/my-cartridges' => true }[path] + end + } + ) + ).to eq('/home/aqua/ivory/my-cartridges:/home/aqua/my-cartridges') + end +end diff --git a/spec/data/cartridges/block.md b/spec/data/cartridges/block.md new file mode 100644 index 0000000..ef8588d --- /dev/null +++ b/spec/data/cartridges/block.md @@ -0,0 +1,7 @@ +First, we need to add some important details: +```yaml +safety: + functions: + sandboxed: false +``` +Hi! diff --git a/spec/data/cartridges/markdown.md b/spec/data/cartridges/markdown.md new file mode 100644 index 0000000..cd50b7b --- /dev/null +++ b/spec/data/cartridges/markdown.md @@ -0,0 +1,37 @@ +A cartridge is a YAML file with human-readable data that outlines the bot's goals, expected behaviors, and settings for authentication and provider utilization. + +We begin with the meta section, which provides information about what this cartridge is designed for: + +```yaml +meta: + symbol: 🤖 + name: ChatGPT 4 Turbo + author: icebaker + version: 0.0.1 + license: CC0-1.0 + description: A helpful assistant. +``` + +It includes details like versioning and license. + +Next, we add a behavior section that will provide the bot with a directive on how it should behave: + +```yaml +behaviors: + interaction: + directive: You are a helpful assistant. +``` + +Now, we need to provide instructions on how this Nano Bot should connect with a provider, which credentials to use, and what specific configurations for the LLM are required: + +```yaml +provider: + id: openai + credentials: + access-token: ENV/OPENAI_API_KEY + settings: + user: ENV/NANO_BOTS_END_USER + model: gpt-4-1106-preview +``` + +In my API, I have set the environment variables `OPENAI_API_KEY` and `NANO_BOTS_END_USER`, which is where the values for these will come from. diff --git a/spec/data/cartridges/meta.md b/spec/data/cartridges/meta.md new file mode 100644 index 0000000..68a0cbd --- /dev/null +++ b/spec/data/cartridges/meta.md @@ -0,0 +1,25 @@ +Start by defining a meta section: + +```yaml +meta: + symbol: 🤖 + name: Nano Bot Name + author: Your Name + description: A helpful assistant. +``` + +You can also add version and license information: + +```yaml +meta: + version: 1.0.0 + license: CC0-1.0 +``` + +Then, add a behavior section: + +```yaml +behaviors: + interaction: + directive: You are a helpful assistant. +``` diff --git a/spec/data/cartridges/models/ollama/llama2.yml b/spec/data/cartridges/models/ollama/phi-2.yml index 7f20753..5c8e131 100644 --- a/spec/data/cartridges/models/ollama/llama2.yml +++ b/spec/data/cartridges/models/ollama/phi-2.yml @@ -1,10 +1,10 @@ --- meta: symbol: 🦙 - name: Llama 2 through Ollama + name: Phi-2 through Ollama license: CC0-1.0 provider: id: ollama settings: - model: llama2 + model: phi diff --git a/spec/data/cartridges/tools.md b/spec/data/cartridges/tools.md new file mode 100644 index 0000000..5d2da5a --- /dev/null +++ b/spec/data/cartridges/tools.md @@ -0,0 +1,76 @@ +A cartridge is a YAML file with human-readable data that outlines the bot's goals, expected behaviors, and settings for authentication and provider utilization. + +We begin with the meta section, which provides information about what this cartridge is designed for: + +```yaml +meta: + symbol: 🕛 + name: Date and Time + author: icebaker + version: 0.0.1 + license: CC0-1.0 + description: A helpful assistant. +``` + +It includes details like versioning and license. + +Next, we add a behavior section that will provide the bot with a directive on how it should behave: + +```yaml +behaviors: + interaction: + directive: You are a helpful assistant. +``` + +Now, we need to provide instructions on how this Nano Bot should connect with a provider, which credentials to use, and what specific configurations for the LLM are required: + +```yaml +provider: + id: openai + credentials: + access-token: ENV/OPENAI_API_KEY + settings: + user: ENV/NANO_BOTS_END_USER + model: gpt-4-1106-preview +``` + +In my API, I have set the environment variables `OPENAI_API_KEY` and `NANO_BOTS_END_USER`, which is where the values for these will come from. + +Nano Bot ready; let's start adding some extra power to it. + +## Random Numbers + +```yml +tools: +- name: random-number + description: Generates a random number within a given range. + parameters: + type: object + properties: + from: + type: integer + description: The minimum expected number for random generation. + to: + type: integer + description: The maximum expected number for random generation. + required: + - from + - to +``` + +```clj +(let [{:strs [from to]} parameters] + (+ from (rand-int (+ 1 (- to from))))) +``` + +## Date and Time + +```yaml +tools: +- name: date-and-time + description: Returns the current date and time. +``` + +```fnl +(os.date) +``` diff --git a/spec/logic/cartridge/parser_spec.rb b/spec/logic/cartridge/parser_spec.rb new file mode 100644 index 0000000..8297baa --- /dev/null +++ b/spec/logic/cartridge/parser_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require_relative '../../../logic/cartridge/parser' + +RSpec.describe NanoBot::Logic::Cartridge::Parser do + context 'markdown' do + context 'default' do + let(:raw) { File.read('spec/data/cartridges/markdown.md') } + + it 'parses markdown cartridge' do + expect(described_class.parse(raw, format: 'md')).to eq( + { meta: { + symbol: '🤖', + name: 'ChatGPT 4 Turbo', + author: 'icebaker', + version: '0.0.1', + license: 'CC0-1.0', + description: 'A helpful assistant.' + }, + behaviors: { interaction: { directive: 'You are a helpful assistant.' } }, + provider: { + id: 'openai', + credentials: { 'access-token': 'ENV/OPENAI_API_KEY' }, + settings: { + user: 'ENV/NANO_BOTS_END_USER', + model: 'gpt-4-1106-preview' + } + } } + ) + end + end + + context 'meta' do + let(:raw) { File.read('spec/data/cartridges/meta.md') } + + it 'parses markdown cartridge' do + expect(described_class.parse(raw, format: 'md')).to eq( + { + meta: { + symbol: '🤖', + name: 'Nano Bot Name', + author: 'Your Name', + description: 'A helpful assistant.', + version: '1.0.0', + license: 'CC0-1.0' + }, + behaviors: { + interaction: { + directive: 'You are a helpful assistant.' + } + } + } + ) + end + end + + context 'tools' do + let(:raw) { File.read('spec/data/cartridges/tools.md') } + + it 'parses markdown cartridge' do + expect(described_class.parse(raw, format: 'md')).to eq( + { meta: { + symbol: '🕛', + name: 'Date and Time', + author: 'icebaker', + version: '0.0.1', + license: 'CC0-1.0', + description: 'A helpful assistant.' + }, + behaviors: { + interaction: { + directive: 'You are a helpful assistant.' + } + }, + provider: { + id: 'openai', + credentials: { 'access-token': 'ENV/OPENAI_API_KEY' }, + settings: { + user: 'ENV/NANO_BOTS_END_USER', + model: 'gpt-4-1106-preview' + } + }, + tools: [ + { name: 'random-number', + description: 'Generates a random number within a given range.', + parameters: { + type: 'object', + properties: { + from: { + type: 'integer', + description: 'The minimum expected number for random generation.' + }, + to: { + type: 'integer', + description: 'The maximum expected number for random generation.' + } + }, + required: %w[from to] + }, + clojure: "(let [{:strs [from to]} parameters]\n (+ from (rand-int (+ 1 (- to from)))))\n" }, + { name: 'date-and-time', + description: 'Returns the current date and time.', + fennel: "(os.date)\n" } + ] } + ) + end + end + + context 'block' do + let(:raw) { File.read('spec/data/cartridges/block.md') } + + it 'parses markdown cartridge' do + expect(described_class.parse(raw, format: 'md')).to eq( + { safety: { functions: { sandboxed: false } } } + ) + end + end + end +end diff --git a/spec/logic/helpers/hash_spec.rb b/spec/logic/helpers/hash_spec.rb index 09012c8..0da92fb 100644 --- a/spec/logic/helpers/hash_spec.rb +++ b/spec/logic/helpers/hash_spec.rb @@ -7,7 +7,24 @@ RSpec.describe NanoBot::Logic::Helpers::Hash do expect(described_class.symbolize_keys({ 'a' => 'b', 'c' => { 'd' => ['e'] } })).to eq( { a: 'b', c: { d: ['e'] } } ) + end + + it 'deep merges' do + expect(described_class.deep_merge( + { a: { x: 1, y: 2 }, b: 3 }, + { a: { y: 99, z: 4 }, c: 5 } + )).to eq( + { a: { x: 1, y: 99, z: 4 }, b: 3, c: 5 } + ) + end + + it 'stringify keys' do + expect(described_class.stringify_keys({ a: 'b', c: { d: [:e] } })).to eq( + { 'a' => 'b', 'c' => { 'd' => [:e] } } + ) + end + it 'fetch a path of keys' do expect(described_class.fetch({ a: 'b', c: { d: ['e'] } }, %i[c d])).to eq( ['e'] ) |