nano-bots 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19a8bc3b2ea8dcd2703a22efb3ac334485536b602e385c3d5cd69cdb6e1bfa1b
4
- data.tar.gz: 76375f04692caacd92d832f843acaf3fb82cf68e0e467674749730a04e30794a
3
+ metadata.gz: 58071f1bd32ba3fb2bfe58b64b392139724f3cd97ab72e8822b3db7ebd4779bc
4
+ data.tar.gz: 60f24c528ced7f151996431288fee4c08b49e099d4fba46181d530ca2fa34581
5
5
  SHA512:
6
- metadata.gz: 9ddbdc1421ce91117a2d16d94794f6111d2ea72503c24ed0f3597c7522a851e7fd7eef08703c5ba8f68826e51e9b34621591232e2d9a5220efda5d026027d413
7
- data.tar.gz: b2a729b278245a971a815c2333cd7676d795a64cdd86aef0d737ad9510c9a78bc8420eaaef9067f305201afbbccaf63e40508832d4cfad7cbf19b91fefc3aa07
6
+ metadata.gz: 02ed6e915d64df284189cfb599e868364ffacffb3b4e5323c77ec284173d0c74f58a925eb292b834184dac523d150bac8670b1414041872c545f51867ea1690d
7
+ data.tar.gz: da8c4426fb19e1f1516eb76647bd179669cfae3640b9d83df4d3f1ed1157127efccee58704fa88870129666f19b211c43558a64ce68cd4b86ca6249b2cc63ae8
data/Gemfile.lock CHANGED
@@ -1,13 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nano-bots (0.0.4)
4
+ nano-bots (0.0.6)
5
5
  babosa (~> 2.0)
6
6
  dotenv (~> 2.8, >= 2.8.1)
7
7
  faraday (~> 2.7, >= 2.7.4)
8
8
  pry (~> 0.14.2)
9
9
  rainbow (~> 3.1, >= 3.1.1)
10
10
  ruby-openai (~> 4.0)
11
+ sweet-moon (~> 0.0.7)
11
12
 
12
13
  GEM
13
14
  remote: https://rubygems.org/
@@ -23,6 +24,7 @@ GEM
23
24
  faraday-multipart (1.0.4)
24
25
  multipart-post (~> 2)
25
26
  faraday-net_http (3.0.2)
27
+ ffi (1.15.5)
26
28
  json (2.6.3)
27
29
  method_source (1.0.0)
28
30
  multipart-post (2.3.0)
@@ -62,17 +64,19 @@ GEM
62
64
  parser (>= 3.2.1.0)
63
65
  rubocop-capybara (2.18.0)
64
66
  rubocop (~> 1.41)
65
- rubocop-factory_bot (2.22.0)
67
+ rubocop-factory_bot (2.23.1)
66
68
  rubocop (~> 1.33)
67
69
  rubocop-rspec (2.22.0)
68
70
  rubocop (~> 1.33)
69
71
  rubocop-capybara (~> 2.17)
70
72
  rubocop-factory_bot (~> 2.22)
71
- ruby-openai (4.0.0)
73
+ ruby-openai (4.1.0)
72
74
  faraday (>= 1)
73
75
  faraday-multipart (>= 1)
74
76
  ruby-progressbar (1.13.0)
75
77
  ruby2_keywords (0.0.5)
78
+ sweet-moon (0.0.7)
79
+ ffi (~> 1.15, >= 1.15.5)
76
80
  unicode-display_width (2.4.2)
77
81
 
78
82
  PLATFORMS
data/README.md CHANGED
@@ -23,13 +23,13 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
23
23
  For a system usage:
24
24
 
25
25
  ```sh
26
- gem install nano-bots -v 0.0.4
26
+ gem install nano-bots -v 0.0.6
27
27
  ```
28
28
 
29
29
  To use it in a project, add it to your `Gemfile`:
30
30
 
31
31
  ```ruby
32
- gem 'nano-bots', '~> 0.0.4'
32
+ gem 'nano-bots', '~> 0.0.6'
33
33
  ```
34
34
 
35
35
  ```sh
@@ -76,7 +76,7 @@ version: '3.7'
76
76
  services:
77
77
  nano-bots:
78
78
  image: ruby:3.2.2-slim-bullseye
79
- command: sh -c "gem install nano-bots -v 0.0.4 && bash"
79
+ command: sh -c "gem install nano-bots -v 0.0.6 && bash"
80
80
  environment:
81
81
  OPENAI_API_ADDRESS: https://api.openai.com
82
82
  OPENAI_API_ACCESS_TOKEN: your-token
@@ -254,5 +254,5 @@ gem build nano-bots.gemspec
254
254
 
255
255
  gem signin
256
256
 
257
- gem push nano-bots-0.0.4.gem
257
+ gem push nano-bots-0.0.6.gem
258
258
  ```
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sweet-moon'
4
+
5
+ module NanoBot
6
+ module Components
7
+ class Adapter
8
+ def self.apply(_direction, params)
9
+ content = params[:content]
10
+
11
+ if params[:fennel] && params[:lua]
12
+ raise StandardError, 'Adapter conflict: You can only use either Lua or Fennel, not both.'
13
+ end
14
+
15
+ if params[:fennel]
16
+ content = fennel(content, params[:fennel])
17
+ elsif params[:lua]
18
+ content = lua(content, params[:lua])
19
+ end
20
+
21
+ "#{params[:prefix]}#{content}#{params[:suffix]}"
22
+ end
23
+
24
+ def self.fennel(content, expression)
25
+ path = "#{File.expand_path('../static/fennel', __dir__)}/?.lua"
26
+ state = SweetMoon::State.new(package_path: path).fennel
27
+ state.fennel.eval("(set _G.adapter (fn [content] #{expression}))")
28
+ adapter = state.get(:adapter)
29
+ adapter.call([content])
30
+ end
31
+
32
+ def self.lua(content, expression)
33
+ state = SweetMoon::State.new
34
+ state.eval("adapter = function(content) return #{expression}; end")
35
+ adapter = state.get(:adapter)
36
+ adapter.call([content])
37
+ end
38
+ end
39
+ end
40
+ end
@@ -29,6 +29,13 @@ module NanoBot
29
29
  path
30
30
  end
31
31
 
32
+ def self.cartridges_path
33
+ [
34
+ ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY', nil),
35
+ "#{user_home!.sub(%r{/$}, '')}/.local/share/nano-bots/cartridges"
36
+ ].uniq.filter { |path| File.directory?(path) }.compact.first
37
+ end
38
+
32
39
  def self.cartridge_path(path)
33
40
  partial = File.join(File.dirname(path), File.basename(path, File.extname(path)))
34
41
 
@@ -43,7 +50,7 @@ module NanoBot
43
50
 
44
51
  partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial)))
45
52
 
46
- partial = path.sub(%r{^\.?/}, '')
53
+ partial = partial.sub(%r{^\.?/}, '')
47
54
 
48
55
  candidates << "#{directory}/#{partial}"
49
56
  candidates << "#{directory}/#{partial}.yml"
@@ -54,7 +61,7 @@ module NanoBot
54
61
 
55
62
  partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial)))
56
63
 
57
- partial = path.sub(%r{^\.?/}, '')
64
+ partial = partial.sub(%r{^\.?/}, '')
58
65
 
59
66
  candidates << "#{directory}/#{partial}"
60
67
  candidates << "#{directory}/#{partial}.yml"
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ module NanoBot
6
+ module Components
7
+ class Stream < StringIO
8
+ def write(*args)
9
+ if @callback
10
+ @accumulated += args.first
11
+ @callback.call(@accumulated, args.first, false)
12
+ end
13
+ super
14
+ end
15
+
16
+ def callback=(block)
17
+ @accumulated = ''
18
+ @callback = block
19
+ end
20
+
21
+ def finish
22
+ flush
23
+ result = string.clone
24
+ truncate(0)
25
+ rewind
26
+
27
+ if @callback
28
+ @callback.call(@accumulated, nil, true)
29
+ @callback = nil
30
+ @accumulated = nil
31
+ end
32
+
33
+ result
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../components/storage'
4
+ require_relative '../logic/helpers/hash'
5
+ require_relative '../logic/cartridge/default'
6
+
7
+ module NanoBot
8
+ module Controllers
9
+ class Cartridges
10
+ def self.all
11
+ files = {}
12
+
13
+ path = Components::Storage.cartridges_path
14
+
15
+ Dir.glob("#{path}/**/*.{yml,yaml}").each do |file|
16
+ files[Pathname.new(file).realpath] = {
17
+ base: path,
18
+ path: Pathname.new(file).realpath
19
+ }
20
+ end
21
+
22
+ cartridges = []
23
+
24
+ files.values.uniq.map do |file|
25
+ cartridge = Logic::Helpers::Hash.symbolize_keys(
26
+ YAML.safe_load(File.read(file[:path]), permitted_classes: [Symbol])
27
+ ).merge({
28
+ system: {
29
+ id: file[:path].to_s.sub(/^#{Regexp.escape(file[:base])}/, '').sub(%r{^/}, '').sub(/\.[^.]+\z/,
30
+ ''),
31
+ path: file[:path],
32
+ base: file[:base]
33
+ }
34
+ })
35
+
36
+ next if cartridge[:meta][:name].nil?
37
+
38
+ cartridges << cartridge
39
+ rescue StandardError => _e
40
+ end
41
+
42
+ cartridges.sort_by { |cartridge| cartridge[:meta][:name] }
43
+
44
+ cartridges.prepend(
45
+ { system: { id: '-' }, meta: { name: 'Default', symbol: '🤖' } }
46
+ )
47
+
48
+ cartridges
49
+ end
50
+ end
51
+ end
52
+ end
@@ -5,6 +5,7 @@ require 'yaml'
5
5
  require_relative '../logic/helpers/hash'
6
6
  require_relative '../components/provider'
7
7
  require_relative '../components/storage'
8
+ require_relative '../components/stream'
8
9
  require_relative './interfaces/repl'
9
10
  require_relative './interfaces/eval'
10
11
  require_relative './session'
@@ -30,16 +31,14 @@ module NanoBot
30
31
  @session.state
31
32
  end
32
33
 
33
- def eval(input)
34
+ def eval(input, &block)
35
+ @stream.callback = block if block && @stream.is_a?(Components::Stream)
36
+
34
37
  Interfaces::Eval.evaluate(input, @cartridge, @session)
35
38
 
36
- return unless @stream.is_a?(StringIO)
39
+ return unless @stream.is_a?(Components::Stream)
37
40
 
38
- @stream.flush
39
- result = @stream.string.clone
40
- @stream.truncate(0)
41
- @stream.rewind
42
- result
41
+ @stream.finish
43
42
  end
44
43
 
45
44
  def repl
@@ -55,7 +54,7 @@ module NanoBot
55
54
 
56
55
  def load_cartridge!(path)
57
56
  elected_path = if path.strip == '-'
58
- File.expand_path('../static/cartridges/default.yml', __dir__)
57
+ File.expand_path('../static/cartridges/baseline.yml', __dir__)
59
58
  else
60
59
  Components::Storage.cartridge_path(path)
61
60
  end
@@ -4,34 +4,21 @@ require 'pry'
4
4
  require 'rainbow'
5
5
 
6
6
  require_relative '../../logic/helpers/hash'
7
+ require_relative '../../logic/cartridge/affixes'
7
8
 
8
9
  module NanoBot
9
10
  module Controllers
10
11
  module Interfaces
11
12
  module Eval
12
13
  def self.evaluate(input, cartridge, session)
13
- prefix = build_prefix(cartridge)
14
- postfix = build_postfix(cartridge)
14
+ prefix = Logic::Cartridge::Affixes.get(cartridge, :eval, :output, :prefix)
15
+ suffix = Logic::Cartridge::Affixes.get(cartridge, :eval, :output, :suffix)
15
16
 
16
17
  session.print(prefix) unless prefix.nil?
17
18
 
18
19
  session.evaluate_and_print(input, mode: 'eval')
19
20
 
20
- session.print(postfix) unless postfix.nil?
21
- end
22
-
23
- def self.build_prefix(cartridge)
24
- eval_interface = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces eval])
25
- return nil if eval_interface.nil?
26
-
27
- eval_interface[:prefix]
28
- end
29
-
30
- def self.build_postfix(cartridge)
31
- eval_interface = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces eval])
32
- return "\n" if eval_interface.nil? || !eval_interface.key?(:postfix) # default
33
-
34
- eval_interface[:postfix]
21
+ session.print(suffix) unless suffix.nil?
35
22
  end
36
23
  end
37
24
  end
@@ -4,19 +4,20 @@ require 'pry'
4
4
  require 'rainbow'
5
5
 
6
6
  require_relative '../../logic/helpers/hash'
7
+ require_relative '../../logic/cartridge/affixes'
7
8
 
8
9
  module NanoBot
9
10
  module Controllers
10
11
  module Interfaces
11
12
  module REPL
12
13
  def self.start(cartridge, session)
13
- prefix = build_prefix(cartridge)
14
- postfix = build_postfix(cartridge)
14
+ prefix = Logic::Cartridge::Affixes.get(cartridge, :repl, :output, :prefix)
15
+ suffix = Logic::Cartridge::Affixes.get(cartridge, :repl, :output, :suffix)
15
16
 
16
17
  if Logic::Helpers::Hash.fetch(cartridge, %i[behaviors boot instruction])
17
18
  session.print(prefix) unless prefix.nil?
18
19
  session.boot(mode: 'repl')
19
- session.print(postfix) unless postfix.nil?
20
+ session.print(suffix) unless suffix.nil?
20
21
  session.print("\n")
21
22
  end
22
23
 
@@ -29,9 +30,9 @@ module NanoBot
29
30
  )
30
31
 
31
32
  Pry.commands.block_command(/(.*)/, 'handler') do |line|
32
- session.print(postfix) unless postfix.nil?
33
+ session.print(prefix) unless prefix.nil?
33
34
  session.evaluate_and_print(line, mode: 'repl')
34
- session.print(postfix) unless postfix.nil?
35
+ session.print(suffix) unless suffix.nil?
35
36
  session.print("\n")
36
37
  session.flush
37
38
  end
@@ -39,20 +40,6 @@ module NanoBot
39
40
  Pry.start
40
41
  end
41
42
 
42
- def self.build_prefix(cartridge)
43
- repl = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl])
44
- return "\n" if repl.nil? || !repl.key?(:prefix) # default
45
-
46
- repl[:prefix]
47
- end
48
-
49
- def self.build_postfix(cartridge)
50
- repl = Logic::Helpers::Hash.fetch(cartridge, %i[interfaces repl])
51
- return "\n" if repl.nil? || !repl.key?(:postfix) # default
52
-
53
- repl[:postfix]
54
- end
55
-
56
43
  def self.build_prompt(prompt)
57
44
  result = ''
58
45
 
@@ -5,7 +5,10 @@ require 'babosa'
5
5
  require 'fileutils'
6
6
 
7
7
  require_relative '../logic/helpers/hash'
8
+ require_relative '../logic/cartridge/streaming'
9
+ require_relative '../logic/cartridge/interaction'
8
10
  require_relative '../components/storage'
11
+ require_relative '../components/adapter'
9
12
 
10
13
  module NanoBot
11
14
  module Controllers
@@ -56,24 +59,22 @@ module NanoBot
56
59
  def evaluate_and_print(message, mode:)
57
60
  behavior = Logic::Helpers::Hash.fetch(@cartridge, %i[behaviors interaction]) || {}
58
61
 
59
- @state[:history] << ({ who: 'user', message: })
62
+ @state[:history] << {
63
+ who: 'user',
64
+ message: Components::Adapter.apply(
65
+ :input, Logic::Cartridge::Interaction.input(@cartridge, mode.to_sym, message)
66
+ )
67
+ }
60
68
 
61
69
  input = { behavior:, history: @state[:history] }
62
70
 
63
71
  process(input, mode:)
64
72
  end
65
73
 
66
- def streaming(interface)
67
- provider = @provider.settings.key?(:stream) ? @provider.settings[:stream] : true
68
- interface = interface.key?(:stream) ? interface[:stream] : true
69
-
70
- provider && interface
71
- end
72
-
73
74
  def process(input, mode:)
74
75
  interface = Logic::Helpers::Hash.fetch(@cartridge, [:interfaces, mode.to_sym]) || {}
75
76
 
76
- streaming = streaming(interface)
77
+ streaming = Logic::Cartridge::Streaming.enabled?(@cartridge, mode.to_sym)
77
78
 
78
79
  input[:interface] = interface
79
80
 
@@ -82,9 +83,18 @@ module NanoBot
82
83
  ready = false
83
84
  @provider.evaluate(input) do |output, finished|
84
85
  updated_at = Time.now
86
+
85
87
  if finished
86
- @state[:history] << output
88
+ @state[:history] << Marshal.load(Marshal.dump(output))
89
+
90
+ output = Logic::Cartridge::Interaction.output(
91
+ @cartridge, mode.to_sym, output, streaming, finished
92
+ )
93
+
94
+ output[:message] = Components::Adapter.apply(:output, output[:message])
95
+
87
96
  self.print(output[:message]) unless streaming
97
+
88
98
  ready = true
89
99
  flush
90
100
  elsif streaming
@@ -3,7 +3,7 @@ version: '3.7'
3
3
  services:
4
4
  nano-bots:
5
5
  image: ruby:3.2.2-slim-bullseye
6
- command: sh -c "gem install nano-bots -v 0.0.4 && bash"
6
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev lua5.4-dev && gem install nano-bots -v 0.0.6 && bash"
7
7
  environment:
8
8
  OPENAI_API_ADDRESS: https://api.openai.com
9
9
  OPENAI_API_ACCESS_TOKEN: your-token
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/hash'
4
+ require_relative './default'
5
+
6
+ module NanoBot
7
+ module Logic
8
+ module Cartridge
9
+ module Adapter
10
+ def self.expression(cartridge, interface, direction, language)
11
+ adapter = [
12
+ {
13
+ exists: (Helpers::Hash.fetch(cartridge, [:interfaces, direction, :adapter]) || {}).key?(language),
14
+ value: Helpers::Hash.fetch(cartridge, [:interfaces, direction, :adapter, language])
15
+ },
16
+ {
17
+ exists: (Helpers::Hash.fetch(cartridge,
18
+ [:interfaces, interface, direction, :adapter]) || {}).key?(language),
19
+ value: Helpers::Hash.fetch(cartridge, [:interfaces, interface, direction, :adapter, language])
20
+ }
21
+ ].filter { |candidate| candidate[:exists] }.last
22
+
23
+ return nil if adapter.nil?
24
+
25
+ adapter[:value]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/hash'
4
+ require_relative './default'
5
+
6
+ module NanoBot
7
+ module Logic
8
+ module Cartridge
9
+ module Affixes
10
+ def self.get(cartridge, interface, direction, kind)
11
+ affix = [
12
+ {
13
+ exists: (Helpers::Hash.fetch(cartridge, [:interfaces, direction]) || {}).key?(kind),
14
+ value: Helpers::Hash.fetch(cartridge, [:interfaces, direction, kind])
15
+ },
16
+ {
17
+ exists: (Helpers::Hash.fetch(cartridge, [:interfaces, interface, direction]) || {}).key?(kind),
18
+ value: Helpers::Hash.fetch(cartridge, [:interfaces, interface, direction, kind])
19
+ }
20
+ ].filter { |candidate| candidate[:exists] }.last
21
+
22
+ if affix.nil?
23
+ return Helpers::Hash.fetch(
24
+ Default.instance.values, [:interfaces, interface, direction, kind]
25
+ )
26
+ end
27
+
28
+ affix[:value]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'singleton'
5
+
6
+ require_relative '../helpers/hash'
7
+
8
+ module NanoBot
9
+ module Logic
10
+ module Cartridge
11
+ class Default
12
+ include Singleton
13
+
14
+ def values
15
+ return @values if @values
16
+
17
+ path = File.expand_path('../../static/cartridges/default.yml', __dir__)
18
+ cartridge = YAML.safe_load(File.read(path), permitted_classes: [Symbol])
19
+ @values = Logic::Helpers::Hash.symbolize_keys(cartridge)
20
+ @values
21
+ end
22
+
23
+ def baseline
24
+ return @baseline if @baseline
25
+
26
+ path = File.expand_path('../../static/cartridges/baseline.yml', __dir__)
27
+ cartridge = YAML.safe_load(File.read(path), permitted_classes: [Symbol])
28
+ @baseline = Logic::Helpers::Hash.symbolize_keys(cartridge)
29
+ @baseline
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sweet-moon'
4
+
5
+ require_relative './affixes'
6
+ require_relative './adapters'
7
+
8
+ module NanoBot
9
+ module Logic
10
+ module Cartridge
11
+ module Interaction
12
+ def self.input(cartridge, interface, content)
13
+ lua = Adapter.expression(cartridge, interface, :input, :lua)
14
+ fennel = Adapter.expression(cartridge, interface, :input, :fennel)
15
+
16
+ prefix = Affixes.get(cartridge, interface, :input, :prefix)
17
+ suffix = Affixes.get(cartridge, interface, :input, :suffix)
18
+
19
+ { content:, prefix:, suffix:, lua:, fennel: }
20
+ end
21
+
22
+ def self.output(cartridge, interface, result, streaming, _finished)
23
+ if streaming
24
+ result[:message] = { content: result[:message], lua: nil, fennel: nil }
25
+ return result
26
+ end
27
+
28
+ lua = Adapter.expression(cartridge, interface, :output, :lua)
29
+ fennel = Adapter.expression(cartridge, interface, :output, :fennel)
30
+
31
+ result[:message] = { content: result[:message], lua:, fennel: }
32
+
33
+ result
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/hash'
4
+
5
+ module NanoBot
6
+ module Logic
7
+ module Cartridge
8
+ module Streaming
9
+ def self.enabled?(cartridge, interface)
10
+ return false if Helpers::Hash.fetch(cartridge, %i[provider settings stream]) == false
11
+
12
+ specific_interface = Helpers::Hash.fetch(cartridge, [:interfaces, interface, :output, :stream])
13
+
14
+ return specific_interface unless specific_interface.nil?
15
+
16
+ interface = Helpers::Hash.fetch(cartridge, %i[interfaces output stream])
17
+
18
+ return interface unless interface.nil?
19
+
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -23,6 +23,10 @@ module NanoBot
23
23
  return nil unless node
24
24
 
25
25
  path.each do |key|
26
+ unless node.is_a?(::Hash)
27
+ node = nil
28
+ break
29
+ end
26
30
  node = node[key]
27
31
  break if node.nil?
28
32
  end
data/nano-bots.gemspec CHANGED
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
37
37
  spec.add_dependency 'pry', '~> 0.14.2'
38
38
  spec.add_dependency 'rainbow', '~> 3.1', '>= 3.1.1'
39
39
  spec.add_dependency 'ruby-openai', '~> 4.0'
40
+ spec.add_dependency 'sweet-moon', '~> 0.0.7'
40
41
 
41
42
  spec.metadata['rubygems_mfa_required'] = 'true'
42
43
  end
@@ -3,12 +3,18 @@
3
3
  require 'dotenv/load'
4
4
 
5
5
  require_relative '../../static/gem'
6
+ require_relative '../../controllers/cartridges'
6
7
  require_relative '../../controllers/instance'
7
8
  require_relative '../../controllers/interfaces/cli'
9
+ require_relative '../../components/stream'
8
10
 
9
11
  module NanoBot
10
12
  def self.new(cartridge: '-', state: '-')
11
- Controllers::Instance.new(cartridge_path: cartridge, state:, stream: StringIO.new)
13
+ Controllers::Instance.new(cartridge_path: cartridge, state:, stream: Components::Stream.new)
14
+ end
15
+
16
+ def self.cartridges
17
+ Controllers::Cartridges.all
12
18
  end
13
19
 
14
20
  def self.cli