Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
source 'https://rubygems.org'
source "https://rubygems.org"

ruby '>= 2.7'
ruby ">= 2.7"

gem 'faraday'
gem 'faraday-net_http_persistent'
gem 'rspec'
gem 'webmock'
gem "faraday"
gem "faraday-net_http_persistent"
gem "rspec"
gem "webmock"
48 changes: 24 additions & 24 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
bigdecimal (3.1.6)
connection_pool (2.4.1)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
bigdecimal (3.2.2)
connection_pool (2.5.3)
crack (1.0.0)
bigdecimal
rexml
diff-lcs (1.5.1)
faraday (2.12.0)
faraday-net_http (>= 2.0, < 3.4)
diff-lcs (1.6.2)
faraday (2.13.1)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.3.0)
net-http
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
faraday-net_http_persistent (2.3.0)
faraday (~> 2.5)
net-http-persistent (>= 4.0.4, < 5)
hashdiff (1.1.0)
json (2.7.2)
logger (1.6.1)
net-http (0.4.1)
hashdiff (1.2.0)
json (2.12.2)
logger (1.7.0)
net-http (0.6.0)
uri
net-http-persistent (4.0.4)
connection_pool (~> 2.2)
public_suffix (5.0.4)
rexml (3.2.6)
rspec (3.13.0)
net-http-persistent (4.0.6)
connection_pool (~> 2.2, >= 2.2.4)
public_suffix (6.0.2)
rexml (3.4.1)
rspec (3.13.1)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-core (3.13.4)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.0)
rspec-mocks (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.0)
uri (0.13.1)
webmock (3.22.0)
rspec-support (3.13.4)
uri (1.0.3)
webmock (3.25.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
Expand Down
157 changes: 109 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,107 +1,168 @@
# RubyAI - OpenAI integration Ruby gem



[![Gem Version](https://badge.fury.io/rb/rubyai.svg)](https://badge.fury.io/rb/rubyai)

[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/alexshapalov/rubyai/blob/main/LICENSE)



## Use the [OpenAI API 🤖 ](https://openai.com/blog/openai-api/) with Ruby! ❤️

Generate text with ChatGPT (Generative Pre-trained Transformer)

Generate text with ChatGPT, Claude and Gemini!




# Installation

Add this line to your application's Gemfile:
install a latest version via Bundler:

```ruby
gem "rubyai"
```ruby
bundle add rubyai
```

And then execute:
```

$ bundle install


And then execute:

Or install with:

$ gem install rubyai
$ bundle install

and require with:



```ruby
require "rubyai"
```
Or install with:

# Usage

- Get your API key from [https://beta.openai.com/account/api-keys](https://beta.openai.com/account/api-keys)
$ gem install rubyai

- If you belong to multiple organizations, you can get your Organization ID from [https://beta.openai.com/account/org-settings](https://beta.openai.com/account/org-settings)

### Quickstart
and require with:

For a quick test you can pass your token directly to a new client:

```ruby
result = RubyAI::Client.new(access_token, messages).call

require "rubyai"

```

### ChatGPT

ChatGPT is a conversational-style text generation model.
You can use it to [generate a response](https://platform.openai.com/docs/api-reference/chat/create) to a sequence of [messages](https://platform.openai.com/docs/guides/chat/introduction):
# Usage

```ruby
api_key = "YOUR API KEY"
messages = "Who is the best chess player in history?"

- Get your API key from [https://beta.openai.com/account/api-keys](https://beta.openai.com/account/api-keys), https://console.anthropic.com, https://aistudio.google.com/apikey

- If you belong to multiple organizations, you can get your Organization ID from [https://beta.openai.com/account/org-settings](https://beta.openai.com/account/org-settings)

### Configuration
Our gem is using separate configurations for every provider it supports, example:
```ruby
# for openai LLM's it could be:
RubyAI.configuration.openai.configure do |config|
config.api = "your-api"
config.model = "o1-mini"
config.temoperature = 0.75
end

result = RubyAI::Client.new(api_key, messages, model: "gpt-4").call
puts result.dig("choices", 0, "message", "content")
# for anthropic:
RubyAI.configuration.anthropic.configure do |config|
config.api = "your-api"
config.model="claude-2"
temperature = 0.75
config.max_tokens = 1000
end

# => As an AI language model, I do not have personal opinions, but according to historical records, Garry Kasparov is often considered as one of the best chess players in history. Other notable players include Magnus Carlsen, Bobby Fischer, and Jose Capablanca.
# for gemini:
RubyAI.configuration.gemini.configure do |config|
config.api = "your-api"
config.model="gemini-1.5-pro"
temperature = 0.75
config.max_tokens = 1000
end
```

You can also pass client variables using the configuration file.
Create configruation file like on example:
### Chat
After configuration you can chat with models by calling `Chat` class, example:
```ruby
configuration = RubyAI::Configuration.new("YOUR API KEY", "Who is the best chess player in history?")
client = RubyAI::Client.new(configuration)
result = client.call
puts result.dig("choices", 0, "message", "content")
```
claude2 = RubyAI::Chat.new('anthropic', model: "claude-2")
claude2.call("Hello world") # => Hash response
# Or

Also (mostly) if you are using Rails you can use configure method:
```ruby
RubyAI.configure do |config|
config.api_key = "YOUR API KEY"
config.messages = "Who is the best chess player in history?"
config.model = "gpt-4o-mini"
end
gpt = RubyAI::Chat.new("openai", "gpt-4", temperature: 1)
gpt.call("Hello world!") # => Hash response
```
###

## Models

We support all popular GPT models:
We support most of the popular GPT models:


```ruby
p RubyAI::Configuration::MODELS.each.each_key
"openai" =>
{"gpt-3.5-turbo" => "gpt-3.5-turbo",
"gpt-4" => "gpt-4",
"gpt-4-32k" => "gpt-4-32k",
"gpt-4-turbo" => "gpt-4-turbo",
"gpt-4o-mini" => "gpt-4o-mini",
"o1-mini" => "o1-mini",
"o1-preview" => "o1-preview",
"text-davinci-003" => "text-davinci-003"},
"anthropic" =>
{"claude-2" => "claude-2",
"claude-instant-100k" => "claude-instant-100k",
"claude-1" => "claude-1",
"claude-1.3" => "claude-1.3",
"claude-1.3-sonnet" => "claude-1.3-sonnet",
"claude-1.3-sonnet-100k" => "claude-1.3-sonnet-100k"},
"gemini" => {"gemini-1.5-pro" => "gemini-1.5-pro", "gemini-1.5-flash" => "gemini-1.5-flash", "gemini-1.0-pro" => "gemini-1.0-pro"}}

```

gpt-4-turbo: A powerful variant of GPT-4 optimized for efficiency and speed, perfect for high-demand tasks.

gpt-4o-mini: A streamlined version of GPT-4, designed to provide a balance between performance and resource efficiency.
### TODO:

o1-mini: A compact, yet effective model that is well-suited for lightweight tasks.

o1-preview: A preview version of the o1 model, offering insights into upcoming advancements and features.
1. Support for Gemini models to be configurated via `configure` block
2. Implement more LLM's support
3. Write an Specs for most of use cases
4. Stream responses


## Development


After checking out the repo, run `bin/setup` to install dependencies. You can run `bin/console` for an interactive prompt that will allow you to experiment.



To install this gem onto your local machine, run `bundle exec rake install`.




## Contributing



Bug reports and pull requests are welcome on GitHub at <https://github.com/alexshapalov/rubyai>. This project is intended to be a safe, welcoming space for collaboration, and contributors.



## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).


The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
25 changes: 21 additions & 4 deletions lib/rubyai.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
require 'faraday'
require 'faraday/net_http_persistent'
require 'json'
require "faraday"
require "faraday/net_http_persistent"
require "json"

require_relative "rubyai/client"
require_relative "rubyai/providers/providers_configuration"
require_relative "rubyai/providers/openai"
require_relative "rubyai/providers/anthropic"
require_relative "rubyai/providers/gemini"
require_relative "rubyai/provider"
require_relative "rubyai/configuration"
require_relative "rubyai/http"
require_relative "rubyai/chat"
require_relative "rubyai/version"

module RubyAI
class Error < StandardError; end

def self.models
Configuration::MODELS
end

def self.configure
yield config
end

def self.config(params = {})
@config ||= Configuration.new(params)
end
end
43 changes: 43 additions & 0 deletions lib/rubyai/chat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module RubyAI
class Chat
attr_accessor :provider, :model, :temperature

def initialize(provider,
model: nil,
temperature: 0.75)
@provider = provider || RubyAI.config.default_provider
@model = model
@temperature = temperature
end

def call(messages)
raise ArgumentError, "Messages cannot be empty" if messages.nil? || messages.empty?

body = HTTP.build_body(messages, @provider, @model, @temperature)
headers = HTTP.build_headers(provider)

response = connection.post do |req|
req.url Configuration::PROVIDERS[@provider, @model]
req.headers.merge!(headers)
req.body = body.to_json
end

JSON.parse(response.body)
end

private

def connection
@connection ||= Faraday.new do |faraday|
faraday.adapter Faraday.default_adapter
faraday.headers["Content-Type"] = "application/json"
end
rescue Faraday::Error => e
raise "Connection error: #{e.message}"
rescue JSON::ParserError => e
raise "Response parsing error: #{e.message}"
rescue StandardError => e
raise "An unexpected error occurred: #{e.message}"
end
end
end
Loading