summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoricebaker <113217272+icebaker@users.noreply.github.com>2023-12-28 21:01:33 -0300
committerGitHub <noreply@github.com>2023-12-28 21:01:33 -0300
commit8b7b90c9601eb3d73ae443142aab2c9d8168cb62 (patch)
tree540270080c6cc6d61b6c2f81e9b70c81f0e60f28
parent347ac029c922fbb3843ec22324527f2c6b3ba9a9 (diff)
parent94d7aa2b726a09abfac85f472eef8921d14c16a9 (diff)
Merge pull request #12 from icebaker/ib-mistral-ai
Adding support for Mistral AI and upgrading gemini-ai
-rw-r--r--Gemfile.lock14
-rw-r--r--README.md327
-rw-r--r--components/provider.rb5
-rw-r--r--components/providers/google.rb21
-rw-r--r--components/providers/mistral.rb115
-rw-r--r--controllers/session.rb1
-rw-r--r--logic/cartridge/streaming.rb2
-rw-r--r--logic/providers/google/tokens.rb2
-rw-r--r--logic/providers/mistral/tokens.rb14
-rw-r--r--logic/providers/openai/tokens.rb2
-rw-r--r--nano-bots.gemspec3
-rw-r--r--spec/logic/cartridge/interaction_spec.rb2
-rw-r--r--static/gem.rb8
13 files changed, 367 insertions, 149 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index be466f6..3355970 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,11 +1,12 @@
PATH
remote: .
specs:
- nano-bots (2.2.0)
+ nano-bots (2.3.0)
babosa (~> 2.0)
concurrent-ruby (~> 1.2, >= 1.2.2)
dotenv (~> 2.8, >= 2.8.1)
- gemini-ai (~> 2.2)
+ gemini-ai (~> 3.1)
+ mistral-ai (~> 1.0)
pry (~> 0.14.2)
rainbow (~> 3.1, >= 3.1.1)
rbnacl (~> 7.1, >= 7.1.1)
@@ -26,7 +27,7 @@ GEM
diff-lcs (1.5.0)
dotenv (2.8.1)
event_stream_parser (1.0.0)
- faraday (2.7.12)
+ faraday (2.8.1)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
@@ -34,9 +35,9 @@ GEM
multipart-post (~> 2)
faraday-net_http (3.0.2)
ffi (1.16.3)
- gemini-ai (2.2.0)
+ gemini-ai (3.1.0)
event_stream_parser (~> 1.0)
- faraday (~> 2.7, >= 2.7.12)
+ faraday (~> 2.8, >= 2.8.1)
googleauth (~> 1.9, >= 1.9.1)
google-cloud-env (2.1.0)
faraday (>= 1.0, < 3.a)
@@ -51,6 +52,9 @@ GEM
jwt (2.7.1)
language_server-protocol (3.17.0.3)
method_source (1.0.0)
+ mistral-ai (1.0.0)
+ event_stream_parser (~> 1.0)
+ faraday (~> 2.8, >= 2.8.1)
multi_json (1.15.0)
multipart-post (2.3.0)
os (1.1.4)
diff --git a/README.md b/README.md
index 0ba3c3c..8804f45 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,29 @@
# Nano Bots 💎 🤖
-A Ruby implementation of the [Nano Bots](https://github.com/icebaker/nano-bots) specification.
+A Ruby implementation of the [Nano Bots](https://github.com/icebaker/nano-bots) specification with support for [OpenAI ChatGPT](https://openai.com/chatgpt), [Mistral AI](https://mistral.ai), and [Google Gemini](https://deepmind.google/technologies/gemini).
-![Ruby Nano Bots](https://user-images.githubusercontent.com/113217272/237839690-7880915a-b287-4484-a75e-0b96284b8a32.png)
-_Image artificially created by Midjourney through a prompt generated by a Nano Bot specialized in Midjourney._
+![Ruby Nano Bots](https://raw.githubusercontent.com/icebaker/assets/main/nano-bots/ruby-nano-bots-canvas.png)
https://user-images.githubusercontent.com/113217272/238141567-c58a240c-7b67-4b3b-864a-0f49bbf6e22f.mp4
- [Setup](#setup)
- [OpenAI ChatGPT](#openai-chatgpt)
+ - [Mistral AI](#mistral-ai)
- [Google Gemini](#google-gemini)
- - [Docker](#docker)
- - [OpenAI ChatGPT](#openai-chatgpt-1)
- - [Google Gemini](#google-gemini-1)
- [Usage](#usage)
- [Command Line](#command-line)
- [Library](#library)
- [Cartridges](#cartridges)
- - [OpenAI ChatGPT](#openai-chatgpt-2)
- - [Google Gemini](#google-gemini-2)
+ - [OpenAI ChatGPT](#openai-chatgpt-1)
+ - [Mistral AI](#mistral-ai-1)
+ - [Google Gemini](#google-gemini-1)
- [Tools (Functions)](#tools-functions)
- [Experimental Clojure Support](#experimental-clojure-support)
- [Marketplace](#marketplace)
+- [Docker](#docker)
+ - [OpenAI ChatGPT](#openai-chatgpt-1)
+ - [Mistral AI](#mistral-ai-1)
+ - [Google Gemini](#google-gemini-1)
- [Security and Privacy](#security-and-privacy)
- [Cryptography](#cryptography)
- [End-user IDs](#end-user-ids)
@@ -51,12 +53,7 @@ bundle install
For credentials and configurations, relevant environment variables can be set in your `.bashrc`, `.zshrc`, or equivalent files, as well as in your Docker Container or System Environment. Example:
-### OpenAI ChatGPT
-
```sh
-export OPENAI_API_ADDRESS=https://api.openai.com
-export OPENAI_API_KEY=your-access-token
-
export NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
export NANO_BOTS_END_USER=your-user
@@ -67,9 +64,6 @@ export NANO_BOTS_END_USER=your-user
Alternatively, if your current directory has a `.env` file with the environment variables, they will be automatically loaded:
```sh
-OPENAI_API_ADDRESS=https://api.openai.com
-OPENAI_API_KEY=your-access-token
-
NANO_BOTS_ENCRYPTION_PASSWORD=UNSAFE
NANO_BOTS_END_USER=your-user
@@ -77,6 +71,38 @@ NANO_BOTS_END_USER=your-user
# NANO_BOTS_CARTRIDGES_DIRECTORY=/home/user/.local/share/nano-bots/cartridges
```
+### OpenAI ChatGPT
+
+You can obtain your credentials on the [OpenAI Platform](https://platform.openai.com).
+
+```sh
+export OPENAI_API_ADDRESS=https://api.openai.com
+export OPENAI_API_KEY=your-access-token
+```
+
+Alternatively, if your current directory has a `.env` file with the environment variables, they will be automatically loaded:
+
+```sh
+OPENAI_API_ADDRESS=https://api.openai.com
+OPENAI_API_KEY=your-access-token
+```
+
+### Mistral AI
+
+You can obtain your credentials on the [Mistral Platform](https://console.mistral.ai).
+
+```sh
+export MISTRAL_API_ADDRESS=https://api.mistral.ai
+export MISTRAL_API_KEY=your-api-key
+```
+
+Alternatively, if your current directory has a `.env` file with the environment variables, they will be automatically loaded:
+
+```sh
+MISTRAL_API_ADDRESS=https://api.mistral.ai
+MISTRAL_API_KEY=your-api-key
+```
+
### Google Gemini
Click [here](https://github.com/gbaptista/gemini-ai#credentials) to learn how to obtain your credentials.
@@ -169,117 +195,6 @@ Alternatively, if your current directory has a `.env` file with the environment
GOOGLE_PROJECT_ID=your-project-id
```
-## Docker
-
-Clone the repository and copy the Docker Compose template:
-
-```
-git clone https://github.com/icebaker/ruby-nano-bots.git
-cd ruby-nano-bots
-cp docker-compose.example.yml docker-compose.yml
-```
-
-Set your provider credentials and choose your desired directory for the cartridges files:
-
-### OpenAI ChatGPT
-
-```yaml
----
-services:
- nano-bots:
- image: ruby:3.2.2-slim-bookworm
- 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.2.0 && bash"
- environment:
- OPENAI_API_ADDRESS: https://api.openai.com
- OPENAI_API_KEY: your-access-token
- NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
- NANO_BOTS_END_USER: your-user
- volumes:
- - ./your-cartridges:/root/.local/share/nano-bots/cartridges
- - ./your-state-path:/root/.local/state/nano-bots
-```
-
-### Google Gemini
-
-#### Option 1: API Key (Generative Language API)
-
-```yaml
----
-services:
- nano-bots:
- image: ruby:3.2.2-slim-bookworm
- 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.2.0 && bash"
- environment:
- GOOGLE_API_KEY: your-api-key
- NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
- NANO_BOTS_END_USER: your-user
- volumes:
- - ./your-cartridges:/root/.local/share/nano-bots/cartridges
- - ./your-state-path:/root/.local/state/nano-bots
-```
-
-#### Option 2: Service Account Credentials File (Vertex AI API)
-
-```yaml
----
-services:
- nano-bots:
- image: ruby:3.2.2-slim-bookworm
- 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.2.0 && bash"
- environment:
- GOOGLE_CREDENTIALS_FILE_PATH: /root/.config/google-credentials.json
- GOOGLE_REGION: us-east4
- NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
- NANO_BOTS_END_USER: your-user
- volumes:
- - ./google-credentials.json:/root/.config/google-credentials.json
- - ./your-cartridges:/root/.local/share/nano-bots/cartridges
- - ./your-state-path:/root/.local/state/nano-bots
-```
-
-#### Option 3: Application Default Credentials (Vertex AI API)
-
-```yaml
----
-services:
- nano-bots:
- image: ruby:3.2.2-slim-bookworm
- 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.2.0 && bash"
- environment:
- GOOGLE_REGION: us-east4
- NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
- NANO_BOTS_END_USER: your-user
- volumes:
- - ./your-cartridges:/root/.local/share/nano-bots/cartridges
- - ./your-state-path:/root/.local/state/nano-bots
-```
-
-#### Custom Project ID
-If you need to manually set a Google Project ID:
-
-```yaml
-environment:
- GOOGLE_PROJECT_ID=your-project-id
-```
-
-### Container
-
-Enter the container:
-```sh
-docker compose run nano-bots
-```
-
-Start playing:
-```sh
-nb - - eval "hello"
-nb - - repl
-
-nb assistant.yml - eval "hello"
-nb assistant.yml - repl
-```
-
-You can exit the REPL by typing `exit`.
-
## Usage
### Command Line
@@ -447,6 +362,32 @@ provider:
model: gpt-4-1106-preview
```
+### Mistral AI
+
+Read the [full specification](https://spec.nbots.io/#/README?id=mistral-ai) for Mistral AI.
+
+```yaml
+---
+meta:
+ symbol: 🤖
+ name: Nano Bot Name
+ author: Your Name
+ version: 1.0.0
+ license: CC0-1.0
+ description: A helpful assistant.
+
+behaviors:
+ interaction:
+ directive: You are a helpful assistant.
+
+provider:
+ id: mistral
+ credentials:
+ api-key: ENV/MISTRAL_API_KEY
+ settings:
+ model: mistral-medium
+```
+
### Google Gemini
Read the [full specification](https://spec.nbots.io/#/README?id=google-gemini) for Google Gemini.
@@ -564,7 +505,7 @@ The randomly generated number is 59.
🤖> |
```
-To successfully use Tools (Functions), you need to specify a provider and a model that supports them. As of the writing of this README, the provider that supports them is [OpenAI](https://platform.openai.com/docs/models), with models `gpt-3.5-turbo-1106` and `gpt-4-1106-preview`, and [Google](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling#supported_models), with the `vertex-ai-api` service and the model `gemini-pro`.
+To successfully use Tools (Functions), you need to specify a provider and a model that supports them. As of the writing of this README, the provider that supports them is [OpenAI](https://platform.openai.com/docs/models), with models `gpt-3.5-turbo-1106` and `gpt-4-1106-preview`, and [Google](https://cloud.google.com/vertex-ai/docs/generative-ai/multimodal/function-calling#supported_models), with the `vertex-ai-api` service and the model `gemini-pro`. Mistral AI does not support tools.
Check the [Nano Bots specification](https://spec.nbots.io/#/README?id=tools-functions-2) to learn more about Tools (Functions).
@@ -609,6 +550,135 @@ safety:
You can explore the Nano Bots [Marketplace](https://nbots.io) to discover new Cartridges that can help you.
+## Docker
+
+Clone the repository and copy the Docker Compose template:
+
+```
+git clone https://github.com/icebaker/ruby-nano-bots.git
+cd ruby-nano-bots
+cp docker-compose.example.yml docker-compose.yml
+```
+
+Set your provider credentials and choose your desired directory for the cartridges files:
+
+### OpenAI ChatGPT
+
+```yaml
+---
+services:
+ nano-bots:
+ image: ruby:3.2.2-slim-bookworm
+ 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.2.0 && bash"
+ environment:
+ OPENAI_API_ADDRESS: https://api.openai.com
+ OPENAI_API_KEY: your-access-token
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
+ NANO_BOTS_END_USER: your-user
+ volumes:
+ - ./your-cartridges:/root/.local/share/nano-bots/cartridges
+ - ./your-state-path:/root/.local/state/nano-bots
+```
+
+### Mistral AI
+
+```yaml
+---
+services:
+ nano-bots:
+ image: ruby:3.2.2-slim-bookworm
+ 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.2.0 && bash"
+ environment:
+ MISTRAL_API_ADDRESS: https://api.mistral.ai
+ MISTRAL_API_KEY: your-api-key
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
+ NANO_BOTS_END_USER: your-user
+ volumes:
+ - ./your-cartridges:/root/.local/share/nano-bots/cartridges
+ - ./your-state-path:/root/.local/state/nano-bots
+```
+
+### Google Gemini
+
+#### Option 1: API Key (Generative Language API)
+
+```yaml
+---
+services:
+ nano-bots:
+ image: ruby:3.2.2-slim-bookworm
+ 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.2.0 && bash"
+ environment:
+ GOOGLE_API_KEY: your-api-key
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
+ NANO_BOTS_END_USER: your-user
+ volumes:
+ - ./your-cartridges:/root/.local/share/nano-bots/cartridges
+ - ./your-state-path:/root/.local/state/nano-bots
+```
+
+#### Option 2: Service Account Credentials File (Vertex AI API)
+
+```yaml
+---
+services:
+ nano-bots:
+ image: ruby:3.2.2-slim-bookworm
+ 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.2.0 && bash"
+ environment:
+ GOOGLE_CREDENTIALS_FILE_PATH: /root/.config/google-credentials.json
+ GOOGLE_REGION: us-east4
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
+ NANO_BOTS_END_USER: your-user
+ volumes:
+ - ./google-credentials.json:/root/.config/google-credentials.json
+ - ./your-cartridges:/root/.local/share/nano-bots/cartridges
+ - ./your-state-path:/root/.local/state/nano-bots
+```
+
+#### Option 3: Application Default Credentials (Vertex AI API)
+
+```yaml
+---
+services:
+ nano-bots:
+ image: ruby:3.2.2-slim-bookworm
+ 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.2.0 && bash"
+ environment:
+ GOOGLE_REGION: us-east4
+ NANO_BOTS_ENCRYPTION_PASSWORD: UNSAFE
+ NANO_BOTS_END_USER: your-user
+ volumes:
+ - ./your-cartridges:/root/.local/share/nano-bots/cartridges
+ - ./your-state-path:/root/.local/state/nano-bots
+```
+
+#### Custom Project ID
+If you need to manually set a Google Project ID:
+
+```yaml
+environment:
+ GOOGLE_PROJECT_ID=your-project-id
+```
+
+### Container
+
+Enter the container:
+```sh
+docker compose run nano-bots
+```
+
+Start playing:
+```sh
+nb - - eval "hello"
+nb - - repl
+
+nb assistant.yml - eval "hello"
+nb assistant.yml - repl
+```
+
+You can exit the REPL by typing `exit`.
+
## Security and Privacy
Each provider will have its own security and privacy policies (e.g. [OpenAI Policy](https://openai.com/policies/api-data-usage-policies)), so you must consult them to understand their implications.
@@ -720,6 +790,7 @@ If you lose your password, you lose your data. It is not possible to recover it
Currently supported providers:
- [x] [Open AI ChatGPT](https://platform.openai.com/docs/api-reference)
+- [x] [Mistral AI](https://docs.mistral.ai/api/)
- [x] [Google Gemini](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini)
- [ ] [Anthropic Claude](https://www.anthropic.com)
- [ ] [Cohere Command](https://cohere.com)
diff --git a/components/provider.rb b/components/provider.rb
index 57f1cca..bdf3639 100644
--- a/components/provider.rb
+++ b/components/provider.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
-require_relative 'providers/openai'
require_relative 'providers/google'
+require_relative 'providers/mistral'
+require_relative 'providers/openai'
module NanoBot
module Components
@@ -12,6 +13,8 @@ module NanoBot
Providers::OpenAI.new(nil, provider[:settings], provider[:credentials], environment:)
when 'google'
Providers::Google.new(provider[:options], provider[:settings], provider[:credentials], environment:)
+ when 'mistral'
+ Providers::Mistral.new(provider[:options], provider[:settings], provider[:credentials], environment:)
else
raise "Unsupported provider \"#{provider[:id]}\""
end
diff --git a/components/providers/google.rb b/components/providers/google.rb
index 25ffbde..3976e2c 100644
--- a/components/providers/google.rb
+++ b/components/providers/google.rb
@@ -6,6 +6,8 @@ require_relative 'base'
require_relative '../../logic/providers/google/tools'
require_relative '../../logic/providers/google/tokens'
+require_relative '../../logic/helpers/hash'
+require_relative '../../logic/cartridge/default'
require_relative 'tools'
@@ -26,9 +28,19 @@ module NanoBot
def initialize(options, settings, credentials, _environment)
@settings = settings
+ gemini_options = options.transform_keys { |key| key.to_s.gsub('-', '_').to_sym }
+
+ unless gemini_options.key?(:stream)
+ gemini_options[:stream] = Logic::Helpers::Hash.fetch(
+ Logic::Cartridge::Default.instance.values, %i[provider settings stream]
+ )
+ end
+
+ gemini_options[:server_sent_events] = gemini_options.delete(:stream)
+
@client = Gemini.new(
credentials: credentials.transform_keys { |key| key.to_s.gsub('-', '_').to_sym },
- options: options.transform_keys { |key| key.to_s.gsub('-', '_').to_sym }
+ options: gemini_options
)
end
@@ -105,6 +117,9 @@ module NanoBot
tools = []
stream_call_back = proc do |event, _parsed, _raw|
+ # TODO: How to better handle finishReason == 'OTHER'?
+ return if event.dig('candidates', 0, 'finishReason') == 'OTHER'
+
partial_content = event.dig('candidates', 0, 'content', 'parts').filter do |part|
part.key?('text')
end.map { |part| part['text'] }.join
@@ -132,7 +147,7 @@ module NanoBot
@client.stream_generate_content(
Logic::Google::Tokens.apply_policies!(cartridge, payload),
- stream: true, &stream_call_back
+ server_sent_events: true, &stream_call_back
)
if tools&.size&.positive?
@@ -156,7 +171,7 @@ module NanoBot
else
result = @client.stream_generate_content(
Logic::Google::Tokens.apply_policies!(cartridge, payload),
- stream: false
+ server_sent_events: false
)
tools = result.dig(0, 'candidates', 0, 'content', 'parts').filter do |part|
diff --git a/components/providers/mistral.rb b/components/providers/mistral.rb
new file mode 100644
index 0000000..9b5c6c4
--- /dev/null
+++ b/components/providers/mistral.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+
+require 'mistral-ai'
+
+require_relative 'base'
+
+require_relative '../../logic/providers/mistral/tokens'
+require_relative '../../logic/helpers/hash'
+require_relative '../../logic/cartridge/default'
+
+module NanoBot
+ module Components
+ module Providers
+ class Mistral < Base
+ attr_reader :settings
+
+ CHAT_SETTINGS = %i[
+ model temperature top_p max_tokens stream safe_mode random_seed
+ ].freeze
+
+ def initialize(options, settings, credentials, _environment)
+ @settings = settings
+
+ mistral_options = if options
+ options.transform_keys { |key| key.to_s.gsub('-', '_').to_sym }
+ else
+ {}
+ end
+
+ unless @settings.key?(:stream)
+ @settings = Marshal.load(Marshal.dump(@settings))
+ @settings[:stream] = Logic::Helpers::Hash.fetch(
+ Logic::Cartridge::Default.instance.values, %i[provider settings stream]
+ )
+ end
+
+ mistral_options[:server_sent_events] = @settings[:stream]
+
+ @client = ::Mistral.new(
+ credentials: credentials.transform_keys { |key| key.to_s.gsub('-', '_').to_sym },
+ options: mistral_options
+ )
+ end
+
+ def evaluate(input, streaming, cartridge, &feedback)
+ messages = input[:history].map do |event|
+ { role: event[:who] == 'user' ? 'user' : 'assistant',
+ content: event[:message],
+ _meta: { at: event[:at] } }
+ end
+
+ %i[backdrop directive].each do |key|
+ next unless input[:behavior][key]
+
+ messages.prepend(
+ { role: key == :directive ? 'system' : 'user',
+ content: input[:behavior][key],
+ _meta: { at: Time.now } }
+ )
+ end
+
+ payload = { messages: }
+
+ CHAT_SETTINGS.each do |key|
+ payload[key] = @settings[key] unless payload.key?(key) || !@settings.key?(key)
+ end
+
+ raise 'Mistral does not support tools.' if input[:tools]
+
+ if streaming
+ content = ''
+
+ stream_call_back = proc do |event, _parsed, _raw|
+ partial_content = event.dig('choices', 0, 'delta', 'content')
+
+ if partial_content
+ content += partial_content
+ feedback.call(
+ { should_be_stored: false,
+ interaction: { who: 'AI', message: partial_content } }
+ )
+ end
+
+ if event.dig('choices', 0, 'finish_reason')
+ feedback.call(
+ { should_be_stored: !(content.nil? || content == ''),
+ interaction: content.nil? || content == '' ? nil : { who: 'AI', message: content },
+ finished: true }
+ )
+ end
+ end
+
+ @client.chat_completions(
+ Logic::Mistral::Tokens.apply_policies!(cartridge, payload),
+ server_sent_events: true, &stream_call_back
+ )
+ else
+ result = @client.chat_completions(
+ Logic::Mistral::Tokens.apply_policies!(cartridge, payload),
+ server_sent_events: false
+ )
+
+ content = result.dig('choices', 0, 'message', 'content')
+
+ feedback.call(
+ { should_be_stored: !(content.nil? || content.to_s.strip == ''),
+ interaction: content.nil? || content == '' ? nil : { who: 'AI', message: content },
+ finished: true }
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/controllers/session.rb b/controllers/session.rb
index e12ab86..dd89d6b 100644
--- a/controllers/session.rb
+++ b/controllers/session.rb
@@ -138,7 +138,6 @@ module NanoBot
feedback[:interaction][:meta][:tool][:action] == 'confirming'
Interfaces::Tool.confirming(self, @cartridge, mode, feedback[:interaction][:meta][:tool])
else
-
if feedback[:interaction] && feedback.dig(:interaction, :meta, :tool, :action)
Interfaces::Tool.dispatch_feedback(
self, @cartridge, mode, feedback[:interaction][:meta][:tool]
diff --git a/logic/cartridge/streaming.rb b/logic/cartridge/streaming.rb
index 6949b3a..0b9b19f 100644
--- a/logic/cartridge/streaming.rb
+++ b/logic/cartridge/streaming.rb
@@ -8,7 +8,7 @@ module NanoBot
module Streaming
def self.enabled?(cartridge, interface)
provider_stream = case Helpers::Hash.fetch(cartridge, %i[provider id])
- when 'openai'
+ when 'openai', 'mistral'
Helpers::Hash.fetch(cartridge, %i[provider settings stream])
when 'google'
Helpers::Hash.fetch(cartridge, %i[provider options stream])
diff --git a/logic/providers/google/tokens.rb b/logic/providers/google/tokens.rb
index 3d5492f..0d74928 100644
--- a/logic/providers/google/tokens.rb
+++ b/logic/providers/google/tokens.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'openai'
-
module NanoBot
module Logic
module Google
diff --git a/logic/providers/mistral/tokens.rb b/logic/providers/mistral/tokens.rb
new file mode 100644
index 0000000..7aa64b7
--- /dev/null
+++ b/logic/providers/mistral/tokens.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module NanoBot
+ module Logic
+ module Mistral
+ module Tokens
+ def self.apply_policies!(_cartridge, payload)
+ payload[:messages] = payload[:messages].map { |message| message.except(:_meta) }
+ payload
+ end
+ end
+ end
+ end
+end
diff --git a/logic/providers/openai/tokens.rb b/logic/providers/openai/tokens.rb
index 60efa60..828b774 100644
--- a/logic/providers/openai/tokens.rb
+++ b/logic/providers/openai/tokens.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'openai'
-
module NanoBot
module Logic
module OpenAI
diff --git a/nano-bots.gemspec b/nano-bots.gemspec
index 15fc8cf..2ff38cb 100644
--- a/nano-bots.gemspec
+++ b/nano-bots.gemspec
@@ -34,7 +34,8 @@ Gem::Specification.new do |spec|
spec.add_dependency 'babosa', '~> 2.0'
spec.add_dependency 'concurrent-ruby', '~> 1.2', '>= 1.2.2'
spec.add_dependency 'dotenv', '~> 2.8', '>= 2.8.1'
- spec.add_dependency 'gemini-ai', '~> 2.2'
+ spec.add_dependency 'gemini-ai', '~> 3.1'
+ spec.add_dependency 'mistral-ai', '~> 1.0'
spec.add_dependency 'pry', '~> 0.14.2'
spec.add_dependency 'rainbow', '~> 3.1', '>= 3.1.1'
spec.add_dependency 'rbnacl', '~> 7.1', '>= 7.1.1'
diff --git a/spec/logic/cartridge/interaction_spec.rb b/spec/logic/cartridge/interaction_spec.rb
index f3ba46e..54fd956 100644
--- a/spec/logic/cartridge/interaction_spec.rb
+++ b/spec/logic/cartridge/interaction_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe NanoBot::Logic::Cartridge::Interaction do
)
end
- it 'prepares the non-streamming output' do
+ it 'prepares the non-streaming output' do
expect(described_class.output(cartridge, :repl, { message: 'hello' }, false, true)).to eq(
{ message: { content: 'hello', fennel: nil, lua: nil, clojure: nil } }
)
diff --git a/static/gem.rb b/static/gem.rb
index e9e5754..b6ce260 100644
--- a/static/gem.rb
+++ b/static/gem.rb
@@ -3,11 +3,11 @@
module NanoBot
GEM = {
name: 'nano-bots',
- version: '2.2.0',
- specification: '2.0.1',
+ version: '2.3.0',
+ specification: '2.1.0',
author: 'icebaker',
- summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots for OpenAI ChatGPT and Google Gemini.',
- 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 and Google Gemini, with support for calling Tools (Functions).',
+ summary: 'Ruby Implementation of Nano Bots: small, AI-powered bots for OpenAI ChatGPT, Mistral AI, and Google Gemini.',
+ 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, Mistral AI, and Google Gemini, with support for calling Tools (Functions).',
github: 'https://github.com/icebaker/ruby-nano-bots',
gem_server: 'https://rubygems.org',
license: 'MIT',