summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authoricebaker <icebaker@proton.me>2023-11-18 19:07:10 -0300
committericebaker <icebaker@proton.me>2023-11-18 19:07:10 -0300
commit8ae78b954350755a47a13133668dba93bac15f37 (patch)
tree9cdc3bb770d778bd8d00675fdbc1f27a6e27e37c /spec
parentab22d1bbe37093912cb7418b3c945153a15f4255 (diff)
adding support for tools
Diffstat (limited to 'spec')
-rw-r--r--spec/data/cartridges/tools.yml28
-rw-r--r--spec/data/providers/openai/tools.yml11
-rw-r--r--spec/logic/cartridge/affixes_spec.rb6
-rw-r--r--spec/logic/cartridge/interaction_spec.rb18
-rw-r--r--spec/logic/cartridge/streaming_spec.rb34
-rw-r--r--spec/logic/cartridge/tools_spec.rb97
-rw-r--r--spec/logic/providers/openai/tools_spec.rb73
-rw-r--r--spec/spec_helper.rb4
8 files changed, 250 insertions, 21 deletions
diff --git a/spec/data/cartridges/tools.yml b/spec/data/cartridges/tools.yml
new file mode 100644
index 0000000..0c2a30b
--- /dev/null
+++ b/spec/data/cartridges/tools.yml
@@ -0,0 +1,28 @@
+---
+tools:
+ - name: get-current-weather
+ type: function
+ description: Get the current weather in a given location.
+ parameters:
+ - name: location
+ - name: unit
+ fennel: |
+ (let [{:location location :unit unit} parameters]
+ (.. "Here is the weather in " location ", in " unit ": 35.8°C."))
+
+ - name: what-time-is-it
+ description: Returns the current date and time.
+ fennel: |
+ (os.date)
+
+ - name: sh
+ description: It has access to computer users' data and can be used to run shell commands, similar to those in a Linux terminal, to extract information. Please be mindful and careful to avoid running dangerous commands on users' computers.
+ parameters:
+ - name: command
+ type: array
+ items:
+ type: string
+ description: An array of strings that represents a shell command along with its arguments or options. For instance, `["df", "-h"]` executes the `df -h` command, where each array element specifies either the command itself or an associated argument/option.
+ clojure: |
+ (require '[clojure.java.shell :refer [sh]])
+ (println (apply sh (get parameters "command")))
diff --git a/spec/data/providers/openai/tools.yml b/spec/data/providers/openai/tools.yml
new file mode 100644
index 0000000..681fb2c
--- /dev/null
+++ b/spec/data/providers/openai/tools.yml
@@ -0,0 +1,11 @@
+---
+- id: call_XYZ
+ type: function
+ function:
+ name: get-current-weather
+ arguments: '{"location":"Tokyo, Japan"}'
+- id: call_ZYX
+ type: function
+ function:
+ name: what-time-is-it
+ arguments: "{}"
diff --git a/spec/logic/cartridge/affixes_spec.rb b/spec/logic/cartridge/affixes_spec.rb
index 8f08e1d..7263008 100644
--- a/spec/logic/cartridge/affixes_spec.rb
+++ b/spec/logic/cartridge/affixes_spec.rb
@@ -6,7 +6,7 @@ require_relative '../../../logic/cartridge/affixes'
RSpec.describe NanoBot::Logic::Cartridge::Affixes do
context 'interfaces' do
- let(:cartridge) { load_cartridge('affixes.yml') }
+ let(:cartridge) { load_symbolized('cartridges/affixes.yml') }
it 'gets the expected affixes' do
expect(described_class.get(cartridge, :repl, :input, :prefix)).to eq('E')
@@ -22,7 +22,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Affixes do
end
context 'interfaces fallback' do
- let(:cartridge) { load_cartridge('affixes.yml') }
+ let(:cartridge) { load_symbolized('cartridges/affixes.yml') }
it 'gets the expected affixes' do
cartridge[:interfaces][:repl][:input].delete(:prefix)
@@ -48,7 +48,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Affixes do
end
context 'interfaces nil' do
- let(:cartridge) { load_cartridge('affixes.yml') }
+ let(:cartridge) { load_symbolized('cartridges/affixes.yml') }
it 'gets the expected affixes' do
cartridge[:interfaces][:repl][:input][:prefix] = nil
diff --git a/spec/logic/cartridge/interaction_spec.rb b/spec/logic/cartridge/interaction_spec.rb
index 347ac45..f3ba46e 100644
--- a/spec/logic/cartridge/interaction_spec.rb
+++ b/spec/logic/cartridge/interaction_spec.rb
@@ -6,41 +6,41 @@ require_relative '../../../logic/cartridge/interaction'
RSpec.describe NanoBot::Logic::Cartridge::Interaction do
context 'input' do
- let(:cartridge) { load_cartridge('affixes.yml') }
+ let(:cartridge) { load_symbolized('cartridges/affixes.yml') }
it 'prepares the input' do
expect(described_class.input(cartridge, :repl, 'hello')).to eq(
- { content: 'hello', fennel: nil, lua: nil, prefix: 'E', suffix: 'F' }
+ { content: 'hello', fennel: nil, lua: nil, clojure: nil, prefix: 'E', suffix: 'F' }
)
expect(described_class.input({}, :repl, 'hello')).to eq(
- { content: 'hello', fennel: nil, lua: nil, prefix: nil, suffix: nil }
+ { content: 'hello', fennel: nil, lua: nil, clojure: nil, prefix: nil, suffix: nil }
)
expect(described_class.input(cartridge, :eval, 'hello')).to eq(
- { content: 'hello', fennel: nil, lua: nil, prefix: 'I', suffix: 'J' }
+ { content: 'hello', fennel: nil, lua: nil, clojure: nil, prefix: 'I', suffix: 'J' }
)
expect(described_class.input({}, :eval, 'hello')).to eq(
- { content: 'hello', fennel: nil, lua: nil, prefix: nil, suffix: nil }
+ { content: 'hello', fennel: nil, lua: nil, clojure: nil, prefix: nil, suffix: nil }
)
end
it 'prepares the non-streamming output' do
expect(described_class.output(cartridge, :repl, { message: 'hello' }, false, true)).to eq(
- { message: { content: 'hello', fennel: nil, lua: nil } }
+ { message: { content: 'hello', fennel: nil, lua: nil, clojure: nil } }
)
expect(described_class.output({}, :repl, { message: 'hello' }, false, true)).to eq(
- { message: { content: 'hello', fennel: nil, lua: nil } }
+ { message: { content: 'hello', fennel: nil, lua: nil, clojure: nil } }
)
expect(described_class.output(cartridge, :eval, { message: 'hello' }, false, true)).to eq(
- { message: { content: 'hello', fennel: nil, lua: nil } }
+ { message: { content: 'hello', fennel: nil, lua: nil, clojure: nil } }
)
expect(described_class.output({}, :eval, { message: 'hello' }, false, true)).to eq(
- { message: { content: 'hello', fennel: nil, lua: nil } }
+ { message: { content: 'hello', fennel: nil, lua: nil, clojure: nil } }
)
end
end
diff --git a/spec/logic/cartridge/streaming_spec.rb b/spec/logic/cartridge/streaming_spec.rb
index e5ad012..466dd0b 100644
--- a/spec/logic/cartridge/streaming_spec.rb
+++ b/spec/logic/cartridge/streaming_spec.rb
@@ -5,8 +5,28 @@ require 'yaml'
require_relative '../../../logic/cartridge/streaming'
RSpec.describe NanoBot::Logic::Cartridge::Streaming do
+ context 'interfaces override' do
+ context 'defaults' do
+ let(:cartridge) { {} }
+
+ it 'uses default values when appropriate' do
+ expect(described_class.enabled?(cartridge, :repl)).to be(true)
+ expect(described_class.enabled?(cartridge, :eval)).to be(true)
+ end
+ end
+
+ context 'top-level overrides' do
+ let(:cartridge) { { interfaces: { output: { stream: false } } } }
+
+ it 'overrides default values when appropriate' do
+ expect(described_class.enabled?(cartridge, :repl)).to be(false)
+ expect(described_class.enabled?(cartridge, :eval)).to be(false)
+ end
+ end
+ end
+
context 'provider' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:provider][:settings][:stream] = false
@@ -15,7 +35,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Streaming do
end
context 'repl' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:interfaces][:repl][:output][:stream] = false
@@ -24,7 +44,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Streaming do
end
context 'interface + repl' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:interfaces][:output][:stream] = false
@@ -34,7 +54,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Streaming do
end
context 'interface' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:interfaces][:output][:stream] = false
@@ -44,7 +64,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Streaming do
end
context '- repl' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:interfaces][:repl][:output].delete(:stream)
@@ -53,7 +73,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Streaming do
end
context '- interface' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:interfaces][:output].delete(:stream)
@@ -63,7 +83,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Streaming do
end
context '- provider' do
- let(:cartridge) { load_cartridge('streaming.yml') }
+ let(:cartridge) { load_symbolized('cartridges/streaming.yml') }
it 'checks if stream is enabled' do
cartridge[:provider][:settings].delete(:stream)
diff --git a/spec/logic/cartridge/tools_spec.rb b/spec/logic/cartridge/tools_spec.rb
new file mode 100644
index 0000000..42b8c57
--- /dev/null
+++ b/spec/logic/cartridge/tools_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'yaml'
+
+require_relative '../../../logic/cartridge/tools'
+
+RSpec.describe NanoBot::Logic::Cartridge::Tools do
+ context 'interfaces override' do
+ context 'defaults' do
+ let(:cartridge) { {} }
+
+ it 'uses default values when appropriate' do
+ expect(described_class.feedback?(cartridge, :repl, :call)).to be(true)
+ expect(described_class.feedback?(cartridge, :eval, :call)).to be(true)
+
+ expect(described_class.feedback?(cartridge, :repl, :response)).to be(false)
+ expect(described_class.feedback?(cartridge, :eval, :response)).to be(false)
+ end
+ end
+
+ context 'top-level overrides' do
+ let(:cartridge) do
+ { interfaces: { tools: { feedback: false } } }
+ end
+
+ it 'overrides default values when appropriate' do
+ expect(described_class.feedback?(cartridge, :repl, :call)).to be(false)
+ expect(described_class.feedback?(cartridge, :eval, :call)).to be(false)
+
+ expect(described_class.feedback?(cartridge, :repl, :response)).to be(false)
+ expect(described_class.feedback?(cartridge, :eval, :response)).to be(false)
+ end
+ end
+
+ context 'top-level overrides' do
+ let(:cartridge) do
+ { interfaces: { tools: { feedback: true } } }
+ end
+
+ it 'overrides default values when appropriate' do
+ expect(described_class.feedback?(cartridge, :repl, :call)).to be(true)
+ expect(described_class.feedback?(cartridge, :eval, :call)).to be(true)
+
+ expect(described_class.feedback?(cartridge, :repl, :response)).to be(true)
+ expect(described_class.feedback?(cartridge, :eval, :response)).to be(true)
+ end
+ end
+
+ context 'top-level-specific overrides' do
+ let(:cartridge) do
+ { interfaces: { tools: { call: { feedback: false }, response: { feedback: true } } } }
+ end
+
+ it 'overrides default values when appropriate' do
+ expect(described_class.feedback?(cartridge, :repl, :call)).to be(false)
+ expect(described_class.feedback?(cartridge, :eval, :call)).to be(false)
+
+ expect(described_class.feedback?(cartridge, :repl, :response)).to be(true)
+ expect(described_class.feedback?(cartridge, :eval, :response)).to be(true)
+ end
+ end
+
+ context 'repl interface overrides' do
+ let(:cartridge) do
+ { interfaces: {
+ tools: { call: { feedback: false }, response: { feedback: true } },
+ repl: { tools: { call: { feedback: true }, response: { feedback: false } } }
+ } }
+ end
+
+ it 'overrides default values when appropriate' do
+ expect(described_class.feedback?(cartridge, :repl, :call)).to be(true)
+ expect(described_class.feedback?(cartridge, :eval, :call)).to be(false)
+
+ expect(described_class.feedback?(cartridge, :repl, :response)).to be(false)
+ expect(described_class.feedback?(cartridge, :eval, :response)).to be(true)
+ end
+ end
+
+ context 'eval interface overrides' do
+ let(:cartridge) do
+ { interfaces: {
+ tools: { call: { feedback: false }, response: { feedback: true } },
+ eval: { tools: { call: { feedback: true }, response: { feedback: false } } }
+ } }
+ end
+
+ it 'overrides default values when appropriate' do
+ expect(described_class.feedback?(cartridge, :repl, :call)).to be(false)
+ expect(described_class.feedback?(cartridge, :eval, :call)).to be(true)
+
+ expect(described_class.feedback?(cartridge, :repl, :response)).to be(true)
+ expect(described_class.feedback?(cartridge, :eval, :response)).to be(false)
+ end
+ end
+ end
+end
diff --git a/spec/logic/providers/openai/tools_spec.rb b/spec/logic/providers/openai/tools_spec.rb
new file mode 100644
index 0000000..c92c374
--- /dev/null
+++ b/spec/logic/providers/openai/tools_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'yaml'
+
+require_relative '../../../../logic/providers/openai/tools'
+
+RSpec.describe NanoBot::Logic::OpenAI::Tools do
+ context 'tools' do
+ let(:cartridge) { load_symbolized('cartridges/tools.yml') }
+
+ context 'adapt' do
+ it 'adapts to OpenAI expected format' do
+ expect(described_class.adapt(cartridge[:tools][0])).to eq(
+ { type: 'function',
+ function: {
+ name: 'get-current-weather',
+ description: 'Get the current weather in a given location.',
+ parameters: {
+ type: 'object',
+ properties: {
+ location: { type: 'string' },
+ unit: { type: 'string' }
+ }
+ }
+ } }
+ )
+
+ expect(described_class.adapt(cartridge[:tools][1])).to eq(
+ { type: 'function',
+ function: {
+ name: 'what-time-is-it',
+ description: 'Returns the current date and time.',
+ parameters: { properties: {}, type: 'object' }
+ } }
+ )
+
+ expect(described_class.adapt(cartridge[:tools][2])).to eq(
+ { type: 'function',
+ function: {
+ name: 'sh',
+ description: "It has access to computer users' data and can be used to run shell commands, similar to those in a Linux terminal, to extract information. Please be mindful and careful to avoid running dangerous commands on users' computers.",
+ parameters: {
+ type: 'object',
+ properties: {
+ command: {
+ type: 'array',
+ description: 'An array of strings that represents a shell command along with its arguments or options. For instance, `["df", "-h"]` executes the `df -h` command, where each array element specifies either the command itself or an associated argument/option.',
+ items: { type: 'string' }
+ }
+ }
+ }
+ } }
+ )
+ end
+ end
+
+ context 'prepare' do
+ let(:tools) { load_symbolized('providers/openai/tools.yml') }
+
+ it 'prepare tools to be executed' do
+ expect(described_class.prepare(cartridge[:tools], tools)).to eq(
+ [{ id: 'call_XYZ',
+ name: 'get-current-weather',
+ type: 'function',
+ parameters: { 'location' => 'Tokyo, Japan' },
+ source: { fennel: "(let [{:location location :unit unit} parameters]\n (.. \"Here is the weather in \" location \", in \" unit \": 35.8°C.\"))\n" } },
+ { id: 'call_ZYX', name: 'what-time-is-it', type: 'function', parameters: {},
+ source: { fennel: "(os.date)\n" } }]
+ )
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ad9038d..cb46554 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -16,8 +16,8 @@ RSpec.configure do |config|
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])
+def load_symbolized(path)
+ cartridge = YAML.safe_load_file("spec/data/#{path}", permitted_classes: [Symbol])
NanoBot::Logic::Helpers::Hash.symbolize_keys(cartridge)
end