open_router 0.1.0 → 0.2.1

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: 3a93aba25cb96e636386ff6124b516fd580fd1535b215d0e5152dd02873f01f5
4
- data.tar.gz: cb2aae6801e85efb8b2e1603797ca2186ed696a019e29839d7fa5509573958e5
3
+ metadata.gz: 617d2bf77818298ec2a2b1f3c56ffd8ae344a969b2e3875c65017cbe9462d084
4
+ data.tar.gz: 0a1f88cc14ed99e96f1a54629ddbfb0139626235824c15edb6aa693df149a39c
5
5
  SHA512:
6
- metadata.gz: 8cbcf5b988d5a709d7de4289aa3dccd470af4975e8f9a37a826ff5df097f0fee04328a9fa3877a3299d1c5849370aeab3f3bada91617efcc10ab13a8116523d3
7
- data.tar.gz: 91d41c29aee465880f59c1b6bcc121f559c929da2c7dec9be55ecc68f992031fa0c096655af0aa56942c9cd4076506e9dbf22d898c274d9fb653222269a871e0
6
+ metadata.gz: '0787bb54df0cf1f38eba5263a58c7840a48d0aa5f4ddb1bef4eb47f95b2b4568ee934b4ab1d666d927a09e94f06a68ad5ade55f54ae6fed0d9af4ef78ec7f0e1'
7
+ data.tar.gz: bd2bf9db26382fea15ab814336e937fb73f1c7fa117d404311c82489a8248254af7ea7a593e74c57ceee43b4a573cf48650887facdf19758e4558c5df94f3a53
data/.rubocop.yml CHANGED
@@ -1,5 +1,29 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.6
2
+ TargetRubyVersion: 3.2
3
+
4
+ Layout/LineLength:
5
+ Enabled: false
6
+
7
+ Metrics/AbcSize:
8
+ Enabled: false
9
+
10
+ Metrics/BlockLength:
11
+ Enabled: false
12
+
13
+ Metrics/CyclomaticComplexity:
14
+ Enabled: false
15
+
16
+ Metrics/MethodLength:
17
+ Enabled: false
18
+
19
+ Metrics/ParameterLists:
20
+ Enabled: false
21
+
22
+ Metrics/PerceivedComplexity:
23
+ Enabled: false
24
+
25
+ Style/Documentation:
26
+ Enabled: false
3
27
 
4
28
  Style/StringLiterals:
5
29
  Enabled: true
@@ -8,6 +32,3 @@ Style/StringLiterals:
8
32
  Style/StringLiteralsInInterpolation:
9
33
  Enabled: true
10
34
  EnforcedStyle: double_quotes
11
-
12
- Layout/LineLength:
13
- Max: 120
data/Gemfile CHANGED
@@ -5,8 +5,9 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in open_router.gemspec
6
6
  gemspec
7
7
 
8
+ gem "activesupport", ">= 6.0"
9
+ gem "dotenv", ">= 2"
10
+ gem "pry", ">= 0.14"
8
11
  gem "rake", "~> 13.0"
9
-
10
12
  gem "rspec", "~> 3.0"
11
-
12
13
  gem "rubocop", "~> 1.21"
data/Gemfile.lock CHANGED
@@ -1,28 +1,55 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- open_router (0.1.0)
4
+ open_router (0.2.1)
5
+ activesupport (>= 6.0)
6
+ dotenv (>= 2)
5
7
  faraday (>= 1)
6
8
  faraday-multipart (>= 1)
7
9
 
8
10
  GEM
9
11
  remote: https://rubygems.org/
10
12
  specs:
13
+ activesupport (7.1.3.2)
14
+ base64
15
+ bigdecimal
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ connection_pool (>= 2.2.5)
18
+ drb
19
+ i18n (>= 1.6, < 2)
20
+ minitest (>= 5.1)
21
+ mutex_m
22
+ tzinfo (~> 2.0)
11
23
  ast (2.4.2)
24
+ base64 (0.2.0)
25
+ bigdecimal (3.1.7)
26
+ coderay (1.1.3)
27
+ concurrent-ruby (1.2.3)
28
+ connection_pool (2.4.1)
12
29
  diff-lcs (1.5.0)
30
+ dotenv (3.1.0)
31
+ drb (2.2.1)
13
32
  faraday (2.7.10)
14
33
  faraday-net_http (>= 2.0, < 3.1)
15
34
  ruby2_keywords (>= 0.0.4)
16
35
  faraday-multipart (1.0.4)
17
36
  multipart-post (~> 2)
18
37
  faraday-net_http (3.0.2)
38
+ i18n (1.14.4)
39
+ concurrent-ruby (~> 1.0)
19
40
  json (2.6.3)
20
41
  language_server-protocol (3.17.0.3)
42
+ method_source (1.0.0)
43
+ minitest (5.22.3)
21
44
  multipart-post (2.3.0)
45
+ mutex_m (0.2.0)
22
46
  parallel (1.23.0)
23
47
  parser (3.2.2.3)
24
48
  ast (~> 2.4.1)
25
49
  racc
50
+ pry (0.14.2)
51
+ coderay (~> 1.1)
52
+ method_source (~> 1.0)
26
53
  racc (1.7.1)
27
54
  rainbow (3.1.1)
28
55
  rake (13.0.6)
@@ -56,13 +83,19 @@ GEM
56
83
  parser (>= 3.2.1.0)
57
84
  ruby-progressbar (1.13.0)
58
85
  ruby2_keywords (0.0.5)
86
+ tzinfo (2.0.6)
87
+ concurrent-ruby (~> 1.0)
59
88
  unicode-display_width (2.4.2)
60
89
 
61
90
  PLATFORMS
62
91
  arm64-darwin-21
92
+ x86_64-linux
63
93
 
64
94
  DEPENDENCIES
95
+ activesupport (>= 6.0)
96
+ dotenv (>= 2)
65
97
  open_router!
98
+ pry (>= 0.14)
66
99
  rake (~> 13.0)
67
100
  rspec (~> 3.0)
68
101
  rubocop (~> 1.21)
data/README.md CHANGED
@@ -10,7 +10,7 @@ The [OpenRouter API](https://openrouter.ai/docs) is a single unified interface f
10
10
  - **Standardized API**: No need to change your code when switching between models or providers. You can even let users choose and pay for their own.
11
11
  - **Easy integration**: This Ruby gem provides a simple and intuitive interface to interact with the OpenRouter API, making it effortless to integrate AI capabilities into your Ruby applications.
12
12
 
13
- 👬 This Ruby library was originally bootstrapped from the [🤖 OpenRouter](https://github.com/alexrudall/anthropic) gem by Alex Rudall, and subsequently extracted from the codebase of my fast-growing AI startup called [Olympia](https://olympia.chat?utm_source=open_router_gem&utm_medium=github) that lets you add AI-powered consultants to your startup!
13
+ 👬 This Ruby library was originally bootstrapped from the [🤖 Anthropic](https://github.com/alexrudall/anthropic) gem by Alex Rudall, and subsequently extracted from the codebase of my fast-growing AI startup called [Olympia](https://olympia.chat?utm_source=open_router_gem&utm_medium=github) that lets you add AI-powered consultants to your startup!
14
14
 
15
15
  🚢 Need someone to develop AI software for you using modern Ruby on Rails? My other company Magma Labs does exactly that: [magmalabs.io](https://www.magmalabs.io/?utm_source=open_router_gem&utm_medium=github). In fact, we also sell off-the-shelf solutions based on my early work on the field, via a platform called [MagmaChat](https://magmachat.ai?utm_source=open_router_gem&utm_medium=github)
16
16
 
@@ -91,7 +91,27 @@ puts response["choices"][0]["message"]["content"]
91
91
 
92
92
  ### Models
93
93
 
94
- Fetch the list of available models from the OpenRouter API:
94
+ Pass an array to the `model` parameter to enable [explicit model routing](https://openrouter.ai/docs#model-routing).
95
+
96
+ ```ruby
97
+ OpenRouter::Client.new.complete(
98
+ [
99
+ { role: "system", content: SYSTEM_PROMPT },
100
+ { role: "user", content: "Provide analysis of the data formatted as JSON:" }
101
+ ],
102
+ model: [
103
+ "mistralai/mixtral-8x7b-instruct:nitro",
104
+ "mistralai/mixtral-8x7b-instruct"
105
+ ],
106
+ extras: {
107
+ response_format: {
108
+ type: "json_object"
109
+ }
110
+ }
111
+ )
112
+ ```
113
+
114
+ [Browse full list of models available](https://openrouter.ai/models) or fetch from the OpenRouter API:
95
115
 
96
116
  ```ruby
97
117
  models = client.models
@@ -110,6 +130,10 @@ puts stats
110
130
  # => {"id"=>"generation-abcdefg", "object"=>"generation", "created"=>1684195200, "model"=>"openrouter/auto", "usage"=>{"prompt_tokens"=>10, "completion_tokens"=>50, "total_tokens"=>60}, "cost"=>0.0006}
111
131
  ```
112
132
 
133
+ ## Errors
134
+
135
+ The client will raise an `OpenRouter::ServerError` in the case of an error returned from a completion (or empty response).
136
+
113
137
  ## Contributing
114
138
 
115
139
  Bug reports and pull requests are welcome on GitHub at <https://github.com/OlympiaAI/open_router>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/OlympiaAI/open_router/blob/main/CODE_OF_CONDUCT.md).
@@ -1,31 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'http'
3
+ require "active_support/core_ext/object/blank"
4
+ require "active_support/core_ext/hash/indifferent_access"
5
+
6
+ require_relative "http"
4
7
 
5
8
  module OpenRouter
9
+ class ServerError < StandardError; end
10
+
6
11
  class Client
7
12
  include OpenRouter::HTTP
8
13
 
9
14
  # Initializes the client with optional configurations.
10
- def initialize
15
+ def initialize(access_token: nil, request_timeout: nil, uri_base: nil, extra_headers: {})
16
+ OpenRouter.configuration.access_token = access_token if access_token
17
+ OpenRouter.configuration.request_timeout = request_timeout if request_timeout
18
+ OpenRouter.configuration.uri_base = uri_base if uri_base
19
+ OpenRouter.configuration.extra_headers = extra_headers if extra_headers.any?
11
20
  yield(OpenRouter.configuration) if block_given?
12
21
  end
13
22
 
14
23
  # Performs a chat completion request to the OpenRouter API.
15
24
  # @param messages [Array<Hash>] Array of message hashes with role and content, like [{role: "user", content: "What is the meaning of life?"}]
16
- # @param model [String] Model identifier, defaults to 'openrouter/auto'
25
+ # @param model [String|Array] Model identifier, or array of model identifiers if you want to fallback to the next model in case of failure
17
26
  # @param providers [Array<String>] Optional array of provider identifiers, ordered by priority
18
27
  # @param transforms [Array<String>] Optional array of strings that tell OpenRouter to apply a series of transformations to the prompt before sending it to the model. Transformations are applied in-order
19
28
  # @param extras [Hash] Optional hash of model-specific parameters to send to the OpenRouter API
20
29
  # @param stream [Proc, nil] Optional callable object for streaming
21
30
  # @return [Hash] The completion response.
22
- def complete(messages, model: 'openrouter/auto', providers: [], transforms: [], extras: {}, stream: nil)
23
- parameters = { model:, messages: }
31
+ def complete(messages, model: "openrouter/auto", providers: [], transforms: [], extras: {}, stream: nil)
32
+ parameters = { messages: }
33
+ if model.is_a?(String)
34
+ parameters[:model] = model
35
+ elsif model.is_a?(Array)
36
+ parameters[:models] = model
37
+ parameters[:route] = "fallback"
38
+ end
24
39
  parameters[:provider] = { provider: { order: providers } } if providers.any?
25
40
  parameters[:transforms] = transforms if transforms.any?
26
41
  parameters[:stream] = stream if stream
27
42
  parameters.merge!(extras)
28
- json_post(path: "/chat/completions", parameters:)
43
+
44
+ json_post(path: "/chat/completions", parameters:).tap do |response|
45
+ raise ServerError, "Empty response from OpenRouter. Might be worth retrying once or twice." if response.blank?
46
+ raise ServerError, response.dig("error", "message") if response.dig("error", "message").present?
47
+ end.with_indifferent_access
29
48
  end
30
49
 
31
50
  # Fetches the list of available models from the OpenRouter API.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenRouter
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/open_router.rb CHANGED
@@ -17,7 +17,7 @@ module OpenRouter
17
17
 
18
18
  DEFAULT_API_VERSION = "v1"
19
19
  DEFAULT_REQUEST_TIMEOUT = 120
20
- DEFAULT_URI_BASE = 'https://openrouter.ai/api'
20
+ DEFAULT_URI_BASE = "https://openrouter.ai/api"
21
21
 
22
22
  def initialize
23
23
  @access_token = nil
data/open_router.gemspec CHANGED
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.files = Dir.chdir(__dir__) do
21
21
  `git ls-files -z`.split("\x0").reject do |f|
22
- (File.expand_path(f) == __FILE__) || f.end_with?('.gem') || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
22
+ (File.expand_path(f) == __FILE__) || f.end_with?(".gem") || f.start_with?(*%w[bin/ test/ spec/ features/ .git
23
+ .circleci appveyor])
23
24
  end
24
25
  end
25
26
 
@@ -27,6 +28,8 @@ Gem::Specification.new do |spec|
27
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
29
  spec.require_paths = ["lib"]
29
30
 
31
+ spec.add_dependency "activesupport", ">= 6.0"
32
+ spec.add_dependency "dotenv", ">= 2"
30
33
  spec.add_dependency "faraday", ">= 1"
31
34
  spec.add_dependency "faraday-multipart", ">= 1"
32
35
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: open_router
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Obie Fernandez
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-19 00:00:00.000000000 Z
11
+ date: 2024-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: faraday
15
43
  requirement: !ruby/object:Gem::Requirement