open_router 0.1.0 → 0.2.2

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: 1ff9756e6285ab09ccaf109eabe0a36747e488450de363ce8ce460b94eb397bc
4
+ data.tar.gz: 3dcb3957fbbe02cc15d0c0806bcbd248b1b3e64e75f48e60789393285f70ed33
5
5
  SHA512:
6
- metadata.gz: 8cbcf5b988d5a709d7de4289aa3dccd470af4975e8f9a37a826ff5df097f0fee04328a9fa3877a3299d1c5849370aeab3f3bada91617efcc10ab13a8116523d3
7
- data.tar.gz: 91d41c29aee465880f59c1b6bcc121f559c929da2c7dec9be55ecc68f992031fa0c096655af0aa56942c9cd4076506e9dbf22d898c274d9fb653222269a871e0
6
+ metadata.gz: 644c0c1fd8ec173af8df82df65496483fa01560a38a8bf1aa23f6473b981333b216bad020f86f14a43f09450803af144891ee696af078ca4a274eaaae6d729ba
7
+ data.tar.gz: 52b2870105b521eae5bd34302d8c95232d018a407021d8dcff47eac0806fd81b6c4beea6191f3cc57e674af06de61cf9cb1f3d98934d4a86cfea31f09bb872f5
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/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.2
data/Gemfile CHANGED
@@ -5,8 +5,15 @@ 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"
14
+
15
+ group :development do
16
+ gem "solargraph-rails", "~> 0.2.0.pre"
17
+ gem "sorbet"
18
+ gem "tapioca", require: false
19
+ end
data/Gemfile.lock CHANGED
@@ -1,32 +1,80 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- open_router (0.1.0)
4
+ open_router (0.2.2)
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
+ backport (1.2.0)
25
+ base64 (0.2.0)
26
+ benchmark (0.3.0)
27
+ bigdecimal (3.1.7)
28
+ coderay (1.1.3)
29
+ concurrent-ruby (1.2.3)
30
+ connection_pool (2.4.1)
12
31
  diff-lcs (1.5.0)
32
+ dotenv (3.1.0)
33
+ drb (2.2.1)
34
+ e2mmap (0.1.0)
35
+ erubi (1.12.0)
13
36
  faraday (2.7.10)
14
37
  faraday-net_http (>= 2.0, < 3.1)
15
38
  ruby2_keywords (>= 0.0.4)
16
39
  faraday-multipart (1.0.4)
17
40
  multipart-post (~> 2)
18
41
  faraday-net_http (3.0.2)
42
+ i18n (1.14.4)
43
+ concurrent-ruby (~> 1.0)
44
+ jaro_winkler (1.5.6)
19
45
  json (2.6.3)
46
+ kramdown (2.4.0)
47
+ rexml
48
+ kramdown-parser-gfm (1.1.0)
49
+ kramdown (~> 2.0)
20
50
  language_server-protocol (3.17.0.3)
51
+ method_source (1.0.0)
52
+ minitest (5.22.3)
21
53
  multipart-post (2.3.0)
54
+ mutex_m (0.2.0)
55
+ netrc (0.11.0)
56
+ nokogiri (1.16.4-arm64-darwin)
57
+ racc (~> 1.4)
58
+ nokogiri (1.16.4-x86_64-linux)
59
+ racc (~> 1.4)
22
60
  parallel (1.23.0)
23
61
  parser (3.2.2.3)
24
62
  ast (~> 2.4.1)
25
63
  racc
64
+ prism (0.24.0)
65
+ pry (0.14.2)
66
+ coderay (~> 1.1)
67
+ method_source (~> 1.0)
26
68
  racc (1.7.1)
27
69
  rainbow (3.1.1)
28
70
  rake (13.0.6)
71
+ rbi (0.1.10)
72
+ prism (>= 0.18.0, < 0.25)
73
+ sorbet-runtime (>= 0.5.9204)
74
+ rbs (2.8.4)
29
75
  regexp_parser (2.8.1)
76
+ reverse_markdown (2.1.1)
77
+ nokogiri
30
78
  rexml (3.2.6)
31
79
  rspec (3.12.0)
32
80
  rspec-core (~> 3.12.0)
@@ -56,16 +104,72 @@ GEM
56
104
  parser (>= 3.2.1.0)
57
105
  ruby-progressbar (1.13.0)
58
106
  ruby2_keywords (0.0.5)
107
+ solargraph (0.50.0)
108
+ backport (~> 1.2)
109
+ benchmark
110
+ bundler (~> 2.0)
111
+ diff-lcs (~> 1.4)
112
+ e2mmap
113
+ jaro_winkler (~> 1.5)
114
+ kramdown (~> 2.3)
115
+ kramdown-parser-gfm (~> 1.1)
116
+ parser (~> 3.0)
117
+ rbs (~> 2.0)
118
+ reverse_markdown (~> 2.0)
119
+ rubocop (~> 1.38)
120
+ thor (~> 1.0)
121
+ tilt (~> 2.0)
122
+ yard (~> 0.9, >= 0.9.24)
123
+ solargraph-rails (0.2.2.pre.4)
124
+ activesupport
125
+ solargraph (>= 0.41.1)
126
+ sorbet (0.5.11342)
127
+ sorbet-static (= 0.5.11342)
128
+ sorbet-runtime (0.5.11342)
129
+ sorbet-static (0.5.11342-universal-darwin)
130
+ sorbet-static (0.5.11342-x86_64-linux)
131
+ sorbet-static-and-runtime (0.5.11342)
132
+ sorbet (= 0.5.11342)
133
+ sorbet-runtime (= 0.5.11342)
134
+ spoom (1.3.0)
135
+ erubi (>= 1.10.0)
136
+ prism (>= 0.19.0)
137
+ sorbet-static-and-runtime (>= 0.5.10187)
138
+ thor (>= 0.19.2)
139
+ tapioca (0.13.1)
140
+ bundler (>= 2.2.25)
141
+ netrc (>= 0.11.0)
142
+ parallel (>= 1.21.0)
143
+ rbi (>= 0.1.4, < 0.2)
144
+ sorbet-static-and-runtime (>= 0.5.11087)
145
+ spoom (>= 1.2.0)
146
+ thor (>= 1.2.0)
147
+ yard-sorbet
148
+ thor (1.3.1)
149
+ tilt (2.3.0)
150
+ tzinfo (2.0.6)
151
+ concurrent-ruby (~> 1.0)
59
152
  unicode-display_width (2.4.2)
153
+ yard (0.9.36)
154
+ yard-sorbet (0.8.1)
155
+ sorbet-runtime (>= 0.5)
156
+ yard (>= 0.9)
60
157
 
61
158
  PLATFORMS
62
159
  arm64-darwin-21
160
+ x86_64-linux
63
161
 
64
162
  DEPENDENCIES
163
+ activesupport (>= 6.0)
164
+ dotenv (>= 2)
65
165
  open_router!
166
+ pry (>= 0.14)
66
167
  rake (~> 13.0)
67
168
  rspec (~> 3.0)
68
169
  rubocop (~> 1.21)
170
+ solargraph-rails (~> 0.2.0.pre)
171
+ sorbet
172
+ tapioca
69
173
 
70
174
  BUNDLED WITH
71
175
  2.4.12
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.2"
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
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = "Ruby library for OpenRouter API."
12
12
  spec.homepage = "https://github.com/OlympiaAI/open_router"
13
13
  spec.license = "MIT"
14
- spec.required_ruby_version = ">= 3.2.1"
14
+ spec.required_ruby_version = ">= 3.2.2"
15
15
 
16
16
  spec.metadata["homepage_uri"] = spec.homepage
17
17
  spec.metadata["source_code_uri"] = "https://github.com/OlympiaAI/open_router"
@@ -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.2
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-04-15 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
@@ -47,6 +75,7 @@ extra_rdoc_files: []
47
75
  files:
48
76
  - ".rspec"
49
77
  - ".rubocop.yml"
78
+ - ".ruby-version"
50
79
  - CHANGELOG.md
51
80
  - CODE_OF_CONDUCT.md
52
81
  - Gemfile
@@ -75,14 +104,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
104
  requirements:
76
105
  - - ">="
77
106
  - !ruby/object:Gem::Version
78
- version: 3.2.1
107
+ version: 3.2.2
79
108
  required_rubygems_version: !ruby/object:Gem::Requirement
80
109
  requirements:
81
110
  - - ">="
82
111
  - !ruby/object:Gem::Version
83
112
  version: '0'
84
113
  requirements: []
85
- rubygems_version: 3.4.12
114
+ rubygems_version: 3.4.10
86
115
  signing_key:
87
116
  specification_version: 4
88
117
  summary: Ruby library for OpenRouter API.