nano-bots 2.5.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 077e36f5c1311458df59a5bd3d25f2f9acc91743d170fdb85977d1bdf7dfe340
4
- data.tar.gz: 8075322ca00d6d948326d6283dd958a456978f731bef75e82c20dd1cc6f4ac86
3
+ metadata.gz: e8c2efff966a76151fb23a83f4c77bc5af780f31898194747c53d8ddd2ff2877
4
+ data.tar.gz: 94642cd59c39f0f9d308e97fe3736bb3d89f529928d3e3896f4c1351a2f10fed
5
5
  SHA512:
6
- metadata.gz: 10cb687eb3cce616701032bb91b298e3ae6567d1ae69bdb5a78964af4f5d1dd575e09c792b06d7710b0abba06ce2597bb4df4fd0c0b3e58575965f9d601416de
7
- data.tar.gz: 5e33d292187460bc88bf4e857d1d8fb0147098b62b8b1e91efe229ce556855185bb2fb370064f87f8b240a2cba38538c8f8eddf1e7f923fbaff10e8683ee123b
6
+ metadata.gz: ff0d2f67a1126a43443cd231184ad3dd02dea9c34178d6aa5da4c49cfdcceb137ab24af7bbaaad83d3e9c61d7019b2ba2950c15b7c1c7d072abc24de8eb8a5a7
7
+ data.tar.gz: 6850151d3af95c232c800e891e4c6ef8ee610030e6f9e45d24c623d497b1d61804f4b709e35b682ad89781fbb5b304d2e9b1048ae0150abaffdec9a0b2084f03
data/Gemfile CHANGED
@@ -8,5 +8,5 @@ group :test, :development do
8
8
  gem 'pry-byebug', '~> 3.10', '>= 3.10.1'
9
9
  gem 'rspec', '~> 3.12'
10
10
  gem 'rubocop', '~> 1.59'
11
- gem 'rubocop-rspec', '~> 2.25'
11
+ gem 'rubocop-rspec', '~> 2.26', '>= 2.26.1'
12
12
  end
data/Gemfile.lock CHANGED
@@ -1,18 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nano-bots (2.5.0)
4
+ nano-bots (3.0.0)
5
5
  babosa (~> 2.0)
6
6
  cohere-ai (~> 1.0, >= 1.0.1)
7
7
  concurrent-ruby (~> 1.2, >= 1.2.2)
8
8
  dotenv (~> 2.8, >= 2.8.1)
9
9
  gemini-ai (~> 3.1, >= 3.1.2)
10
10
  maritaca-ai (~> 1.0)
11
- mistral-ai (~> 1.1)
11
+ mistral-ai (~> 1.1, >= 1.1.1)
12
12
  ollama-ai (~> 1.0)
13
13
  pry (~> 0.14.2)
14
14
  rainbow (~> 3.1, >= 3.1.1)
15
15
  rbnacl (~> 7.1, >= 7.1.1)
16
+ redcarpet (~> 3.6)
16
17
  ruby-openai (~> 6.3, >= 6.3.1)
17
18
  sweet-moon (~> 0.0.7)
18
19
 
@@ -23,7 +24,6 @@ GEM
23
24
  public_suffix (>= 2.0.2, < 6.0)
24
25
  ast (2.4.2)
25
26
  babosa (2.0.0)
26
- base64 (0.2.0)
27
27
  byebug (11.1.3)
28
28
  coderay (1.1.3)
29
29
  cohere-ai (1.0.1)
@@ -32,13 +32,12 @@ GEM
32
32
  diff-lcs (1.5.0)
33
33
  dotenv (2.8.1)
34
34
  event_stream_parser (1.0.0)
35
- faraday (2.8.1)
36
- base64
37
- faraday-net_http (>= 2.0, < 3.1)
38
- ruby2_keywords (>= 0.0.4)
35
+ faraday (2.9.0)
36
+ faraday-net_http (>= 2.0, < 3.2)
39
37
  faraday-multipart (1.0.4)
40
38
  multipart-post (~> 2)
41
- faraday-net_http (3.0.2)
39
+ faraday-net_http (3.1.0)
40
+ net-http
42
41
  ffi (1.16.3)
43
42
  gemini-ai (3.1.2)
44
43
  event_stream_parser (~> 1.0)
@@ -59,16 +58,18 @@ GEM
59
58
  maritaca-ai (1.0.0)
60
59
  faraday (~> 2.8, >= 2.8.1)
61
60
  method_source (1.0.0)
62
- mistral-ai (1.1.0)
61
+ mistral-ai (1.1.1)
63
62
  event_stream_parser (~> 1.0)
64
63
  faraday (~> 2.8, >= 2.8.1)
65
64
  multi_json (1.15.0)
66
65
  multipart-post (2.3.0)
66
+ net-http (0.4.1)
67
+ uri
67
68
  ollama-ai (1.0.0)
68
69
  faraday (~> 2.8)
69
70
  os (1.1.4)
70
71
  parallel (1.24.0)
71
- parser (3.3.0.0)
72
+ parser (3.3.0.2)
72
73
  ast (~> 2.4.1)
73
74
  racc
74
75
  pry (0.14.2)
@@ -82,7 +83,8 @@ GEM
82
83
  rainbow (3.1.1)
83
84
  rbnacl (7.1.1)
84
85
  ffi
85
- regexp_parser (2.8.3)
86
+ redcarpet (3.6.0)
87
+ regexp_parser (2.9.0)
86
88
  rexml (3.2.6)
87
89
  rspec (3.12.0)
88
90
  rspec-core (~> 3.12.0)
@@ -112,8 +114,8 @@ GEM
112
114
  parser (>= 3.2.1.0)
113
115
  rubocop-capybara (2.20.0)
114
116
  rubocop (~> 1.41)
115
- rubocop-factory_bot (2.25.0)
116
- rubocop (~> 1.33)
117
+ rubocop-factory_bot (2.25.1)
118
+ rubocop (~> 1.41)
117
119
  rubocop-rspec (2.26.1)
118
120
  rubocop (~> 1.40)
119
121
  rubocop-capybara (~> 2.17)
@@ -123,7 +125,6 @@ GEM
123
125
  faraday (>= 1)
124
126
  faraday-multipart (>= 1)
125
127
  ruby-progressbar (1.13.0)
126
- ruby2_keywords (0.0.5)
127
128
  signet (0.18.0)
128
129
  addressable (~> 2.8)
129
130
  faraday (>= 0.17.5, < 3.a)
@@ -132,6 +133,7 @@ GEM
132
133
  sweet-moon (0.0.7)
133
134
  ffi (~> 1.15, >= 1.15.5)
134
135
  unicode-display_width (2.5.0)
136
+ uri (0.13.0)
135
137
 
136
138
  PLATFORMS
137
139
  x86_64-linux
@@ -141,7 +143,7 @@ DEPENDENCIES
141
143
  pry-byebug (~> 3.10, >= 3.10.1)
142
144
  rspec (~> 3.12)
143
145
  rubocop (~> 1.59)
144
- rubocop-rspec (~> 2.25)
146
+ rubocop-rspec (~> 2.26, >= 2.26.1)
145
147
 
146
148
  BUNDLED WITH
147
149
  2.4.22
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Nano Bots 💎 🤖
2
2
 
3
- An implementation of the [Nano Bots](https://spec.nbots.io) specification with support for [Cohere Command](https://cohere.com), [Google Gemini](https://deepmind.google/technologies/gemini), [Maritaca AI MariTalk](https://www.maritaca.ai), [Mistral AI](https://mistral.ai), [Ollama](https://ollama.ai), [OpenAI ChatGPT](https://openai.com/chatgpt), and others.
3
+ An implementation of the [Nano Bots](https://spec.nbots.io) specification with support for [Cohere Command](https://cohere.com), [Google Gemini](https://deepmind.google/technologies/gemini), [Maritaca AI MariTalk](https://www.maritaca.ai), [Mistral AI](https://mistral.ai), [Ollama](https://ollama.ai), [OpenAI ChatGPT](https://openai.com/chatgpt), and others, with support for calling tools (functions).
4
4
 
5
5
  ![Ruby Nano Bots](https://raw.githubusercontent.com/icebaker/assets/main/nano-bots/ruby-nano-bots-canvas.png)
6
6
 
@@ -9,7 +9,7 @@ https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b
9
9
  ## TL;DR and Quick Start
10
10
 
11
11
  ```sh
12
- gem install nano-bots -v 2.5.0
12
+ gem install nano-bots -v 3.0.0
13
13
  ```
14
14
 
15
15
  ```bash
@@ -34,13 +34,22 @@ well. How can I assist you?
34
34
  ---
35
35
  meta:
36
36
  symbol: 🤖
37
- name: ChatGPT
37
+ name: Nano Bot Name
38
+ author: Your Name
39
+ version: 1.0.0
40
+ license: CC0-1.0
41
+ description: A helpful assistant.
42
+
43
+ behaviors:
44
+ interaction:
45
+ directive: You are a helpful assistant.
38
46
 
39
47
  provider:
40
48
  id: openai
41
49
  credentials:
42
50
  access-token: ENV/OPENAI_API_KEY
43
51
  settings:
52
+ user: ENV/NANO_BOTS_END_USER
44
53
  model: gpt-4-1106-preview
45
54
  ```
46
55
 
@@ -50,7 +59,7 @@ nb gpt.yml - eval "hi"
50
59
  ```
51
60
 
52
61
  ```ruby
53
- gem 'nano-bots', '~> 2.5.0'
62
+ gem 'nano-bots', '~> 3.0.0'
54
63
  ```
55
64
 
56
65
  ```ruby
@@ -241,13 +250,13 @@ end
241
250
  To install the CLI on your system:
242
251
 
243
252
  ```sh
244
- gem install nano-bots -v 2.5.0
253
+ gem install nano-bots -v 3.0.0
245
254
  ```
246
255
 
247
256
  To use it in a Ruby project as a library, add to your `Gemfile`:
248
257
 
249
258
  ```ruby
250
- gem 'nano-bots', '~> 2.5.0'
259
+ gem 'nano-bots', '~> 3.0.0'
251
260
  ```
252
261
 
253
262
  ```sh
@@ -260,8 +269,8 @@ For credentials and configurations, relevant environment variables can be set in
260
269
  export NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
261
270
  export NANO_BOTS_END_USER=your-user
262
271
 
263
- # export NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
264
- # export NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
272
+ # export NANO_BOTS_STATE_PATH=/home/user/.local/state/nano-bots
273
+ # export NANO_BOTS_CARTRIDGES_PATH=/home/user/.local/share/nano-bots/cartridges
265
274
  ```
266
275
 
267
276
  Alternatively, if your current directory has a `.env` file with the environment variables, they will be automatically loaded:
@@ -270,8 +279,8 @@ Alternatively, if your current directory has a `.env` file with the environment
270
279
  NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
271
280
  NANO_BOTS_END_USER=your-user
272
281
 
273
- # NANO_BOTS_STATE_DIRECTORY=/home/user/.local/state/nano-bots
274
- # NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
282
+ # NANO_BOTS_STATE_PATH=/home/user/.local/state/nano-bots
283
+ # NANO_BOTS_CARTRIDGES_PATH=/home/user/.local/share/nano-bots/cartridges
275
284
  ```
276
285
 
277
286
  ### Cohere Command
@@ -954,7 +963,7 @@ cd ruby-nano-bots
954
963
  cp docker-compose.example.yml docker-compose.yml
955
964
  ```
956
965
 
957
- Set your provider credentials and choose your desired directory for the cartridges files:
966
+ Set your provider credentials and choose your desired path for the cartridges files:
958
967
 
959
968
  ### Cohere Command Container
960
969
 
@@ -963,7 +972,7 @@ Set your provider credentials and choose your desired directory for the cartridg
963
972
  services:
964
973
  nano-bots:
965
974
  image: ruby:3.2.2-slim-bookworm
966
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
975
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
967
976
  environment:
968
977
  COHERE_API_KEY: your-api-key
969
978
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
@@ -980,7 +989,7 @@ services:
980
989
  services:
981
990
  nano-bots:
982
991
  image: ruby:3.2.2-slim-bookworm
983
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
992
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
984
993
  environment:
985
994
  MARITACA_API_KEY: your-api-key
986
995
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
@@ -997,7 +1006,7 @@ services:
997
1006
  services:
998
1007
  nano-bots:
999
1008
  image: ruby:3.2.2-slim-bookworm
1000
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
1009
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
1001
1010
  environment:
1002
1011
  MISTRAL_API_KEY: your-api-key
1003
1012
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
@@ -1009,21 +1018,23 @@ services:
1009
1018
 
1010
1019
  ### Ollama Container
1011
1020
 
1012
- Remember that your `localhost` is inaccessible from inside Docker. You need to either establish [inter-container networking](https://docs.docker.com/compose/networking/) or use the [host's address](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host), depending on where the Ollama server is running.
1021
+ Remember that your `localhost` is by default inaccessible from inside Docker. You need to either establish [inter-container networking](https://docs.docker.com/compose/networking/), use the [host's address](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host), or use the [host network](https://docs.docker.com/network/network-tutorial-host/), depending on where the Ollama server is running and your preferences.
1013
1022
 
1014
1023
  ```yaml
1015
1024
  ---
1016
1025
  services:
1017
1026
  nano-bots:
1018
1027
  image: ruby:3.2.2-slim-bookworm
1019
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
1028
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
1020
1029
  environment:
1021
- OLLAMA_API_ADDRESS: http://host.docker.internal:11434
1030
+ OLLAMA_API_ADDRESS: http://localhost:11434
1022
1031
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
1023
1032
  NANO_BOTS_END_USER: your-user
1024
1033
  volumes:
1025
1034
  - ./your-cartridges:/root/.local/share/nano-bots/cartridges
1026
1035
  - ./your-state-path:/root/.local/state/nano-bots
1036
+ # If you are running the Ollama server on your localhost:
1037
+ network_mode: host # WARNING: Be careful, this may be a security risk.
1027
1038
  ```
1028
1039
 
1029
1040
  ### OpenAI ChatGPT Container
@@ -1033,7 +1044,7 @@ services:
1033
1044
  services:
1034
1045
  nano-bots:
1035
1046
  image: ruby:3.2.2-slim-bookworm
1036
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
1047
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
1037
1048
  environment:
1038
1049
  OPENAI_API_KEY: your-access-token
1039
1050
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
@@ -1052,7 +1063,7 @@ services:
1052
1063
  services:
1053
1064
  nano-bots:
1054
1065
  image: ruby:3.2.2-slim-bookworm
1055
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
1066
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
1056
1067
  environment:
1057
1068
  GOOGLE_API_KEY: your-api-key
1058
1069
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
@@ -1069,7 +1080,7 @@ services:
1069
1080
  services:
1070
1081
  nano-bots:
1071
1082
  image: ruby:3.2.2-slim-bookworm
1072
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
1083
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
1073
1084
  environment:
1074
1085
  GOOGLE_CREDENTIALS_FILE_PATH: /root/.config/google-credentials.json
1075
1086
  GOOGLE_REGION: us-east4
@@ -1088,7 +1099,7 @@ services:
1088
1099
  services:
1089
1100
  nano-bots:
1090
1101
  image: ruby:3.2.2-slim-bookworm
1091
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
1102
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
1092
1103
  environment:
1093
1104
  GOOGLE_REGION: us-east4
1094
1105
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
@@ -1137,6 +1148,12 @@ bundle exec ruby spec/tasks/run-model.rb spec/data/cartridges/models/openai/gpt-
1137
1148
  bundle exec ruby spec/tasks/run-model.rb spec/data/cartridges/models/openai/gpt-4-turbo.yml stream
1138
1149
  ```
1139
1150
 
1151
+ If you face issues upgrading gem versions:
1152
+
1153
+ ```sh
1154
+ bundle install --full-index
1155
+ ```
1156
+
1140
1157
  ### Publish to RubyGems
1141
1158
 
1142
1159
  ```bash
@@ -1144,5 +1161,5 @@ gem build nano-bots.gemspec
1144
1161
 
1145
1162
  gem signin
1146
1163
 
1147
- gem push nano-bots-2.5.0.gem
1164
+ gem push nano-bots-3.0.0.gem
1148
1165
  ```
@@ -45,7 +45,7 @@ module NanoBot
45
45
  def self.clojure(source:, parameters:, values:, safety:)
46
46
  ensure_safety!(safety)
47
47
 
48
- raise 'TODO: sandboxed Clojure through Babashka not implemented' if safety[:sandboxed]
48
+ raise 'Sandboxed Clojure not supported.' if safety[:sandboxed]
49
49
 
50
50
  raise 'invalid Clojure parameter name' if parameters.include?('injected-parameters')
51
51
 
@@ -140,7 +140,7 @@ module NanoBot
140
140
  begin
141
141
  @client.chat(parameters: Logic::OpenAI::Tokens.apply_policies!(cartridge, payload))
142
142
  rescue StandardError => e
143
- raise e.class, e.response[:body] if e.response && e.response[:body]
143
+ raise e.class, e.response[:body] if e.respond_to?(:response) && e.response && e.response[:body]
144
144
 
145
145
  raise e
146
146
  end
@@ -148,7 +148,7 @@ module NanoBot
148
148
  begin
149
149
  result = @client.chat(parameters: Logic::OpenAI::Tokens.apply_policies!(cartridge, payload))
150
150
  rescue StandardError => e
151
- raise e.class, e.response[:body] if e.response && e.response[:body]
151
+ raise e.class, e.response[:body] if e.respond_to?(:response) && e.response && e.response[:body]
152
152
 
153
153
  raise e
154
154
  end
@@ -8,6 +8,8 @@ require_relative 'crypto'
8
8
  module NanoBot
9
9
  module Components
10
10
  class Storage
11
+ EXTENSIONS = %w[yml yaml markdown mdown mkdn md].freeze
12
+
11
13
  def self.end_user(cartridge, environment)
12
14
  user = ENV.fetch('NANO_BOTS_END_USER', nil)
13
15
 
@@ -35,7 +37,9 @@ module NanoBot
35
37
 
36
38
  def self.build_path_and_ensure_state_file!(key, cartridge, environment: {})
37
39
  path = [
40
+ Logic::Helpers::Hash.fetch(cartridge, %i[state path]),
38
41
  Logic::Helpers::Hash.fetch(cartridge, %i[state directory]),
42
+ ENV.fetch('NANO_BOTS_STATE_PATH', nil),
39
43
  ENV.fetch('NANO_BOTS_STATE_DIRECTORY', nil)
40
44
  ].find do |candidate|
41
45
  !candidate.nil? && !candidate.empty?
@@ -64,32 +68,59 @@ module NanoBot
64
68
  path
65
69
  end
66
70
 
67
- def self.cartridges_path
68
- [
69
- ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY', nil),
70
- "#{user_home!.sub(%r{/$}, '')}/.local/share/nano-bots/cartridges"
71
- ].compact.uniq.filter { |path| File.directory?(path) }.compact.first
71
+ def self.cartridges_path(components: {})
72
+ components[:directory?] = ->(path) { File.directory?(path) } unless components.key?(:directory?)
73
+ components[:ENV] = ENV unless components.key?(:ENV)
74
+
75
+ default = "#{user_home!(components:).sub(%r{/$}, '')}/.local/share/nano-bots/cartridges"
76
+
77
+ from_environment = [
78
+ components[:ENV].fetch('NANO_BOTS_CARTRIDGES_PATH', nil),
79
+ components[:ENV].fetch('NANO_BOTS_CARTRIDGES_DIRECTORY', nil)
80
+ ].compact
81
+
82
+ elected = [
83
+ from_environment.empty? ? nil : from_environment.join(':'),
84
+ default
85
+ ].compact.uniq.filter do |path|
86
+ path.split(':').any? { |candidate| components[:directory?].call(candidate) }
87
+ end.compact.first
88
+
89
+ return default unless elected
90
+
91
+ elected = elected.split(':').filter do |path|
92
+ components[:directory?].call(path)
93
+ end.compact
94
+
95
+ elected.size.positive? ? elected.join(':') : default
72
96
  end
73
97
 
74
98
  def self.cartridge_path(path)
75
99
  partial = File.join(File.dirname(path), File.basename(path, File.extname(path)))
76
100
 
77
- candidates = [
78
- path,
79
- "#{partial}.yml",
80
- "#{partial}.yaml"
81
- ]
101
+ candidates = [path]
82
102
 
83
- unless ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY', nil).nil?
84
- directory = ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY').sub(%r{/$}, '')
103
+ EXTENSIONS.each do |extension|
104
+ candidates << "#{partial}.#{extension}"
105
+ end
106
+
107
+ directories = [
108
+ ENV.fetch('NANO_BOTS_CARTRIDGES_PATH', nil),
109
+ ENV.fetch('NANO_BOTS_CARTRIDGES_DIRECTORY', nil)
110
+ ].compact.map do |directory|
111
+ directory.split(':')
112
+ end.flatten.map { |directory| directory.sub(%r{/$}, '') }
85
113
 
114
+ directories.each do |directory|
86
115
  partial = File.join(File.dirname(partial), File.basename(partial, File.extname(partial)))
87
116
 
88
117
  partial = partial.sub(%r{^\.?/}, '')
89
118
 
90
119
  candidates << "#{directory}/#{partial}"
91
- candidates << "#{directory}/#{partial}.yml"
92
- candidates << "#{directory}/#{partial}.yaml"
120
+
121
+ EXTENSIONS.each do |extension|
122
+ candidates << "#{directory}/#{partial}.#{extension}"
123
+ end
93
124
  end
94
125
 
95
126
  directory = "#{user_home!.sub(%r{/$}, '')}/.local/share/nano-bots/cartridges"
@@ -99,8 +130,10 @@ module NanoBot
99
130
  partial = partial.sub(%r{^\.?/}, '')
100
131
 
101
132
  candidates << "#{directory}/#{partial}"
102
- candidates << "#{directory}/#{partial}.yml"
103
- candidates << "#{directory}/#{partial}.yaml"
133
+
134
+ EXTENSIONS.each do |extension|
135
+ candidates << "#{directory}/#{partial}.#{extension}"
136
+ end
104
137
 
105
138
  candidates = candidates.uniq
106
139
 
@@ -109,7 +142,9 @@ module NanoBot
109
142
  end
110
143
  end
111
144
 
112
- def self.user_home!
145
+ def self.user_home!(components: {})
146
+ return components[:home] if components[:home]
147
+
113
148
  [Dir.home, `echo ~`.strip, '~'].find do |candidate|
114
149
  !candidate.nil? && !candidate.empty?
115
150
  end
@@ -3,35 +3,43 @@
3
3
  require_relative '../components/storage'
4
4
  require_relative '../logic/helpers/hash'
5
5
  require_relative '../logic/cartridge/default'
6
+ require_relative '../logic/cartridge/parser'
6
7
 
7
8
  module NanoBot
8
9
  module Controllers
9
10
  class Cartridges
10
- def self.all
11
+ def self.load(path)
12
+ Logic::Cartridge::Parser.parse(File.read(path), format: File.extname(path))
13
+ end
14
+
15
+ def self.all(components: {})
11
16
  files = {}
12
17
 
13
- path = Components::Storage.cartridges_path
18
+ paths = Components::Storage.cartridges_path(components:)
14
19
 
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
+ paths.split(':').each do |path|
21
+ Dir.glob("#{path}/**/*.{yml,yaml,markdown,mdown,mkdn,md}").each do |file|
22
+ files[Pathname.new(file).realpath] = {
23
+ base: path,
24
+ path: Pathname.new(file).realpath
25
+ }
26
+ end
20
27
  end
21
28
 
22
29
  cartridges = []
23
30
 
24
31
  files.values.uniq.map do |file|
25
- cartridge = Logic::Helpers::Hash.symbolize_keys(
26
- YAML.safe_load_file(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
- })
32
+ cartridge = load(file[:path]).merge(
33
+ {
34
+ system: {
35
+ id: file[:path].to_s.sub(
36
+ /^#{Regexp.escape(file[:base])}/, ''
37
+ ).sub(%r{^/}, '').sub(/\.[^.]+\z/, ''),
38
+ path: file[:path],
39
+ base: file[:base]
40
+ }
41
+ }
42
+ )
35
43
 
36
44
  next if cartridge[:meta][:name].nil?
37
45
 
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
-
5
3
  require_relative '../logic/helpers/hash'
6
4
  require_relative '../components/provider'
7
5
  require_relative '../components/storage'
8
6
  require_relative '../components/stream'
7
+ require_relative 'cartridges'
9
8
  require_relative 'interfaces/repl'
10
9
  require_relative 'interfaces/eval'
11
10
  require_relative 'session'
@@ -83,13 +82,11 @@ module NanoBot
83
82
  raise StandardError, "Cartridge file not found: \"#{path}\""
84
83
  end
85
84
 
86
- @cartridge = YAML.safe_load_file(elected_path, permitted_classes: [Symbol])
85
+ @cartridge = Cartridges.load(elected_path)
87
86
  end
88
87
 
89
88
  @safe_cartridge = Marshal.load(Marshal.dump(@cartridge))
90
89
 
91
- @cartridge = Logic::Helpers::Hash.symbolize_keys(@cartridge)
92
-
93
90
  inject_environment_variables!(@cartridge)
94
91
  end
95
92
 
@@ -2,27 +2,31 @@
2
2
  services:
3
3
  nano-bots:
4
4
  image: ruby:3.2.2-slim-bookworm
5
- command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 2.5.0 && bash"
5
+ command: sh -c "apt-get update && apt-get install -y --no-install-recommends build-essential libffi-dev libsodium-dev lua5.4-dev curl && curl -s https://raw.githubusercontent.com/babashka/babashka/master/install | bash && gem install nano-bots -v 3.0.0 && bash"
6
6
  environment:
7
7
  COHERE_API_KEY: your-api-key
8
8
 
9
- GOOGLE_API_KEY: your-api-key
9
+ # GOOGLE_API_KEY: your-api-key
10
10
 
11
- GOOGLE_CREDENTIALS_FILE_PATH: /root/.config/google-credentials.json
12
- GOOGLE_PROJECT_ID: your-project-id
11
+ # GOOGLE_CREDENTIALS_FILE_PATH: /root/.config/google-credentials.json
12
+ # GOOGLE_PROJECT_ID: your-project-id
13
13
  GOOGLE_REGION: us-east4
14
14
 
15
15
  MARITACA_API_KEY: 'your-api-key'
16
16
 
17
17
  MISTRAL_API_KEY: your-api-key
18
18
 
19
- OLLAMA_API_ADDRESS: http://host.docker.internal:11434
19
+ OLLAMA_API_ADDRESS: http://localhost:11434
20
20
 
21
21
  OPENAI_API_KEY: your-access-token
22
22
 
23
23
  NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
24
24
  NANO_BOTS_END_USER: your-user
25
+
25
26
  volumes:
26
27
  - ./google-credentials.json:/root/.config/google-credentials.json
27
28
  - ./your-cartridges:/root/.local/share/nano-bots/cartridges
28
29
  - ./your-state-path:/root/.local/state/nano-bots
30
+
31
+ # If you are running the Ollama server on your localhost:
32
+ # network_mode: host # WARNING: Be careful, this may be a security risk.
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ require 'redcarpet'
6
+ require 'redcarpet/render_strip'
7
+
8
+ module NanoBot
9
+ module Logic
10
+ module Cartridge
11
+ module Parser
12
+ def self.parse(raw, format:)
13
+ normalized = format.to_s.downcase.gsub('.', '').strip
14
+
15
+ if %w[yml yaml].include?(normalized)
16
+ yaml(raw)
17
+ elsif %w[markdown mdown mkdn md].include?(normalized)
18
+ markdown(raw)
19
+ else
20
+ raise "Unknown cartridge format: '#{format}'"
21
+ end
22
+ end
23
+
24
+ def self.markdown(raw)
25
+ yaml_source = []
26
+
27
+ tools = []
28
+
29
+ blocks = Markdown.new.render(raw).blocks
30
+
31
+ previous_block_is_tool = false
32
+
33
+ blocks.each do |block|
34
+ if block[:language] == 'yaml'
35
+ parsed = Logic::Helpers::Hash.symbolize_keys(
36
+ YAML.safe_load(block[:source], permitted_classes: [Symbol])
37
+ )
38
+
39
+ if parsed.key?(:tools) && parsed[:tools].is_a?(Array) && !parsed[:tools].empty?
40
+ previous_block_is_tool = true
41
+
42
+ tools.concat(parsed[:tools])
43
+
44
+ parsed.delete(:tools)
45
+
46
+ unless parsed.empty?
47
+ yaml_source << YAML.dump(
48
+ Logic::Helpers::Hash.stringify_keys(parsed)
49
+ ).gsub(/^---/, '') # TODO: Is this safe enough?
50
+ end
51
+ else
52
+ yaml_source << block[:source]
53
+ previous_block_is_tool = false
54
+ nil
55
+ end
56
+ elsif previous_block_is_tool
57
+ tools.last[block[:language].to_sym] = block[:source]
58
+ previous_block_is_tool = false
59
+ end
60
+ end
61
+
62
+ unless tools.empty?
63
+ yaml_source << YAML.dump(
64
+ Logic::Helpers::Hash.stringify_keys({ tools: })
65
+ ).gsub(/^---/, '') # TODO: Is this safe enough?
66
+ end
67
+
68
+ cartridge = {}
69
+
70
+ yaml_source.each do |source|
71
+ cartridge = Logic::Helpers::Hash.deep_merge(cartridge, yaml(source))
72
+ end
73
+
74
+ cartridge
75
+ end
76
+
77
+ def self.yaml(raw)
78
+ Logic::Helpers::Hash.symbolize_keys(
79
+ YAML.safe_load(raw, permitted_classes: [Symbol])
80
+ )
81
+ end
82
+
83
+ class Renderer < Redcarpet::Render::Base
84
+ LANGUAGES_MAP = {
85
+ 'yml' => 'yaml',
86
+ 'yaml' => 'yaml',
87
+ 'lua' => 'lua',
88
+ 'fnl' => 'fennel',
89
+ 'fennel' => 'fennel',
90
+ 'clj' => 'clojure',
91
+ 'clojure' => 'clojure'
92
+ }.freeze
93
+
94
+ LANGUAGES = LANGUAGES_MAP.keys.freeze
95
+
96
+ def initialize(...)
97
+ super(...)
98
+ @_nano_bots_blocks = []
99
+ end
100
+
101
+ attr_reader :_nano_bots_blocks
102
+
103
+ def block_code(code, language)
104
+ key = language.to_s.downcase.strip
105
+
106
+ return nil unless LANGUAGES.include?(key)
107
+
108
+ @_nano_bots_blocks << { language: LANGUAGES_MAP[key], source: code }
109
+
110
+ nil
111
+ end
112
+ end
113
+
114
+ class Markdown
115
+ attr_reader :markdown
116
+
117
+ def initialize
118
+ @renderer = Renderer.new
119
+ @markdown = Redcarpet::Markdown.new(@renderer, fenced_code_blocks: true)
120
+ end
121
+
122
+ def blocks
123
+ @renderer._nano_bots_blocks
124
+ end
125
+
126
+ def render(raw)
127
+ @markdown.render(raw.gsub(/```\w/, "\n\n\\0"))
128
+ self
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -4,6 +4,16 @@ module NanoBot
4
4
  module Logic
5
5
  module Helpers
6
6
  module Hash
7
+ def self.deep_merge(hash1, hash2)
8
+ hash1.merge(hash2) do |_key, old_val, new_val|
9
+ if old_val.is_a?(::Hash) && new_val.is_a?(::Hash)
10
+ deep_merge(old_val, new_val)
11
+ else
12
+ new_val
13
+ end
14
+ end
15
+ end
16
+
7
17
  def self.symbolize_keys(object)
8
18
  case object
9
19
  when ::Hash
@@ -17,6 +27,19 @@ module NanoBot
17
27
  end
18
28
  end
19
29
 
30
+ def self.stringify_keys(object)
31
+ case object
32
+ when ::Hash
33
+ object.each_with_object({}) do |(key, value), result|
34
+ result[key.to_s] = stringify_keys(value)
35
+ end
36
+ when Array
37
+ object.map { |e| stringify_keys(e) }
38
+ else
39
+ object
40
+ end
41
+ end
42
+
20
43
  def self.fetch(object, path)
21
44
  node = object
22
45
 
data/nano-bots.gemspec CHANGED
@@ -37,12 +37,13 @@ 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 'rbnacl', '~> 7.1', '>= 7.1.1'
40
+ spec.add_dependency 'redcarpet', '~> 3.6'
40
41
  spec.add_dependency 'sweet-moon', '~> 0.0.7'
41
42
 
42
43
  spec.add_dependency 'cohere-ai', '~> 1.0', '>= 1.0.1'
43
44
  spec.add_dependency 'gemini-ai', '~> 3.1', '>= 3.1.2'
44
45
  spec.add_dependency 'maritaca-ai', '~> 1.0'
45
- spec.add_dependency 'mistral-ai', '~> 1.1'
46
+ spec.add_dependency 'mistral-ai', '~> 1.1', '>= 1.1.1'
46
47
  spec.add_dependency 'ollama-ai', '~> 1.0'
47
48
  spec.add_dependency 'ruby-openai', '~> 6.3', '>= 6.3.1'
48
49
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../controllers/cartridges'
4
+
5
+ module NanoBot
6
+ module Cartridges
7
+ def self.all(components: {})
8
+ Controllers::Cartridges.all(components:)
9
+ end
10
+
11
+ def self.load(path)
12
+ Controllers::Cartridges.load(path)
13
+ end
14
+ end
15
+ end
@@ -8,6 +8,7 @@ require_relative '../../controllers/instance'
8
8
  require_relative '../../controllers/security'
9
9
  require_relative '../../controllers/interfaces/cli'
10
10
  require_relative '../../components/stream'
11
+ require_relative 'nano-bots/cartridges'
11
12
 
12
13
  module NanoBot
13
14
  def self.new(cartridge: '-', state: '-', environment: {})
@@ -24,7 +25,7 @@ module NanoBot
24
25
  end
25
26
 
26
27
  def self.cartridges
27
- Controllers::Cartridges.all
28
+ Cartridges
28
29
  end
29
30
 
30
31
  def self.cli
data/static/gem.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  module NanoBot
4
4
  GEM = {
5
5
  name: 'nano-bots',
6
- version: '2.5.0',
7
- specification: '2.3.0',
6
+ version: '3.0.0',
7
+ specification: '3.0.0',
8
8
  author: 'icebaker',
9
9
  summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots for OpenAI ChatGPT, Ollama, Mistral AI, Cohere Command, Maritaca AI MariTalk, and Google Gemini.',
10
10
  description: 'Ruby Implementation of Nano Bots: small, AI-powered bots that can be easily shared as a single file, designed to support multiple providers such as OpenAI ChatGPT, Ollama, Mistral AI, Cohere Command, Maritaca AI MariTalk, and Google Gemini, with support for calling Tools (Functions).',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nano-bots
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - icebaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-07 00:00:00.000000000 Z
11
+ date: 2024-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: babosa
@@ -118,6 +118,20 @@ dependencies:
118
118
  - - ">="
119
119
  - !ruby/object:Gem::Version
120
120
  version: 7.1.1
121
+ - !ruby/object:Gem::Dependency
122
+ name: redcarpet
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '3.6'
128
+ type: :runtime
129
+ prerelease: false
130
+ version_requirements: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '3.6'
121
135
  - !ruby/object:Gem::Dependency
122
136
  name: sweet-moon
123
137
  requirement: !ruby/object:Gem::Requirement
@@ -193,6 +207,9 @@ dependencies:
193
207
  - - "~>"
194
208
  - !ruby/object:Gem::Version
195
209
  version: '1.1'
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: 1.1.1
196
213
  type: :runtime
197
214
  prerelease: false
198
215
  version_requirements: !ruby/object:Gem::Requirement
@@ -200,6 +217,9 @@ dependencies:
200
217
  - - "~>"
201
218
  - !ruby/object:Gem::Version
202
219
  version: '1.1'
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: 1.1.1
203
223
  - !ruby/object:Gem::Dependency
204
224
  name: ollama-ai
205
225
  requirement: !ruby/object:Gem::Requirement
@@ -281,6 +301,7 @@ files:
281
301
  - logic/cartridge/default.rb
282
302
  - logic/cartridge/fetch.rb
283
303
  - logic/cartridge/interaction.rb
304
+ - logic/cartridge/parser.rb
284
305
  - logic/cartridge/safety.rb
285
306
  - logic/cartridge/streaming.rb
286
307
  - logic/cartridge/tools.rb
@@ -296,6 +317,7 @@ files:
296
317
  - logic/providers/openai/tools.rb
297
318
  - nano-bots.gemspec
298
319
  - ports/dsl/nano-bots.rb
320
+ - ports/dsl/nano-bots/cartridges.rb
299
321
  - ports/dsl/nano-bots/cli.rb
300
322
  - static/cartridges/baseline.yml
301
323
  - static/cartridges/default.yml