openrouter_client 0.1.1 → 0.1.3

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: 1dfa50003cbe411f1e237d47c2872e053bbe649253abe52df64da017071bf791
4
- data.tar.gz: 6279dcb4868bf470016c0df6cfe45fd3c5ebc19284cb7eb54d8d8d6c6ca78bb1
3
+ metadata.gz: a13939cbccbd1e6a70514d6d422987b702c313d3db1c7226e61966da6c858acb
4
+ data.tar.gz: edfcd5e0eb7f703b91ca93a04de8f3cf33b0ae219119a65064acbed22c1ce731
5
5
  SHA512:
6
- metadata.gz: 20fa68b2374b7be8f14b0833fa794d3f2d52f0aea344a2707ab9d3330a6a8c43da36e2f7d27681af2a8a2a8ecfeb926bdb70b7f86819e904b38b76aa1c4f2156
7
- data.tar.gz: ecc2aecf1224b2c5ddc1207780ebbc9014a07fd568969d2fda199cdfeaa46ea93ec8badbdacfca74cb6357c3f98ed171e144f584aef2782bfdf50c811ddab100
6
+ metadata.gz: 5edd0c39d7b2a1c57db82dcd4ab0d5b1e44042b496e54ff1c5b8b79b6268c2b5964b443a7297735e2692ed4388fff70a26d357c4cbabfd2b47eee502ca5422ba
7
+ data.tar.gz: 86724cc812f08258dd292cd467887c2a94d5866bb670e8394345ed8da2bdd5d596e98760d4a14f432be3d41c9e4a8f5b1585f58ee88ff2874c5eb515fbb672ac
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openrouter_client (0.1.1)
4
+ openrouter_client (0.1.3)
5
5
  faraday (>= 1)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -215,6 +215,16 @@ model = OpenRouter::Model.find_by!(id: "openai/gpt-4")
215
215
  puts model.name # => "GPT-4"
216
216
  puts model.context_length # => 8192
217
217
  puts model.input_price # => 0.00003 (per token)
218
+
219
+ # Endpoints (available providers for this model)
220
+ model.endpoints.each do |endpoint|
221
+ puts endpoint.provider_name # => "OpenAI"
222
+ puts endpoint.context_length # => 8192
223
+ puts endpoint.prompt_price # => 0.00003
224
+ puts endpoint.completion_price # => 0.00006
225
+ puts endpoint.available? # => true
226
+ puts endpoint.supports?(:tools) # => true
227
+ end
218
228
  puts model.output_price # => 0.00006 (per token)
219
229
  puts model.free? # => false
220
230
 
@@ -232,6 +242,44 @@ model = OpenRouter::Model.find("openai/gpt-4")
232
242
  completion = model.complete(messages: [{ role: "user", content: "Hello!" }])
233
243
  ```
234
244
 
245
+ ### Embeddings
246
+
247
+ Generate vector embeddings from text for semantic search, RAG, and more:
248
+
249
+ ```ruby
250
+ # Single text embedding
251
+ embedding = OpenRouter::Embedding.create!(
252
+ model: "openai/text-embedding-3-small",
253
+ input: "The quick brown fox jumps over the lazy dog"
254
+ )
255
+
256
+ puts embedding.vector # => [0.123, -0.456, ...]
257
+ puts embedding.dimensions # => 1536
258
+ puts embedding.total_tokens # => 9
259
+
260
+ # Batch embedding (multiple texts)
261
+ embedding = OpenRouter::Embedding.create!(
262
+ model: "openai/text-embedding-3-small",
263
+ input: [
264
+ "Machine learning is a subset of AI",
265
+ "Deep learning uses neural networks"
266
+ ]
267
+ )
268
+
269
+ embedding.vectors.each_with_index do |vector, i|
270
+ puts "Text #{i}: #{vector.length} dimensions"
271
+ end
272
+
273
+ # Access individual embedding data
274
+ embedding.data.each do |item|
275
+ puts "Index: #{item.index}, Dimensions: #{item.dimensions}"
276
+ end
277
+
278
+ # List available embedding models
279
+ models = OpenRouter::Embedding.models
280
+ models.each { |m| puts m["id"] }
281
+ ```
282
+
235
283
  ### Generation Stats
236
284
 
237
285
  Query detailed usage information for a completion:
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenRouter
4
+ # Generate vector embeddings from text using OpenRouter's unified embeddings API.
5
+ # Embeddings transform text into high-dimensional vectors where semantically similar
6
+ # texts are positioned closer together in vector space.
7
+ class Embedding
8
+ EMBEDDINGS_PATH = "/embeddings"
9
+ EMBEDDING_MODELS_PATH = "/embeddings/models"
10
+
11
+ # @return [String] The model used for embedding
12
+ attr_reader :model
13
+ # @return [Array<EmbeddingData>] The embedding data objects
14
+ attr_reader :data
15
+ # @return [Hash, nil] Usage statistics
16
+ attr_reader :usage
17
+
18
+ # @param attributes [Hash] Raw response from OpenRouter API
19
+ def initialize(attributes)
20
+ @model = attributes["model"]
21
+ @usage = attributes["usage"]
22
+ @data = build_data(attributes["data"])
23
+ end
24
+
25
+ # Generate embeddings for the given input.
26
+ # @param model [String] The embedding model to use (e.g., "openai/text-embedding-3-small")
27
+ # @param input [String, Array<String>] Text or array of texts to embed
28
+ # @param client [OpenRouter::Client] HTTP client
29
+ # @param options [Hash] Additional options (e.g., provider preferences)
30
+ # @return [OpenRouter::Embedding]
31
+ def self.create!(model:, input:, client: OpenRouter.client, **options)
32
+ payload = {
33
+ model: model,
34
+ input: input,
35
+ **options
36
+ }
37
+
38
+ response = client.post(EMBEDDINGS_PATH, payload)
39
+ new(response)
40
+ end
41
+
42
+ # List available embedding models.
43
+ # @param client [OpenRouter::Client] HTTP client
44
+ # @return [Array<Hash>] Array of model information
45
+ def self.models(client: OpenRouter.client)
46
+ response = client.get(EMBEDDING_MODELS_PATH)
47
+ Array(response && response["data"])
48
+ end
49
+
50
+ # Get the first embedding vector (convenience method for single input).
51
+ # @return [Array<Float>, nil]
52
+ def vector
53
+ @data&.first&.embedding
54
+ end
55
+
56
+ # Get all embedding vectors.
57
+ # @return [Array<Array<Float>>]
58
+ def vectors
59
+ @data&.map(&:embedding) || []
60
+ end
61
+
62
+ # Get the dimension of the embedding vectors.
63
+ # @return [Integer, nil]
64
+ def dimensions
65
+ vector&.length
66
+ end
67
+
68
+ # Get the total tokens used.
69
+ # @return [Integer, nil]
70
+ def total_tokens
71
+ @usage&.dig("total_tokens")
72
+ end
73
+
74
+ # Get the prompt tokens used.
75
+ # @return [Integer, nil]
76
+ def prompt_tokens
77
+ @usage&.dig("prompt_tokens")
78
+ end
79
+
80
+ private
81
+
82
+ def build_data(data_array)
83
+ return [] unless data_array
84
+
85
+ data_array.map { |attrs| EmbeddingData.new(attrs) }
86
+ end
87
+ end
88
+
89
+ # Represents a single embedding result within an Embedding response.
90
+ class EmbeddingData
91
+ # @return [Integer] Index of this embedding in the batch
92
+ attr_reader :index
93
+ # @return [Array<Float>] The embedding vector
94
+ attr_reader :embedding
95
+
96
+ # @param attributes [Hash] Raw attributes from API response
97
+ def initialize(attributes)
98
+ @index = attributes["index"]
99
+ @embedding = attributes["embedding"]
100
+ end
101
+
102
+ # Get the dimension of this embedding vector.
103
+ # @return [Integer]
104
+ def dimensions
105
+ @embedding&.length || 0
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenRouter
4
+ # Represents a specific endpoint/provider for a model.
5
+ # Models can have multiple endpoints from different providers with different pricing and capabilities.
6
+ class Endpoint
7
+ # @return [String] Endpoint name
8
+ attr_reader :name
9
+ # @return [String] Model name
10
+ attr_reader :model_name
11
+ # @return [Integer] Context length in tokens
12
+ attr_reader :context_length
13
+ # @return [Hash] Pricing information
14
+ attr_reader :pricing
15
+ # @return [String] Provider name (e.g., "OpenAI", "Azure")
16
+ attr_reader :provider_name
17
+ # @return [String, nil] Tag
18
+ attr_reader :tag
19
+ # @return [Hash, nil] Quantization info
20
+ attr_reader :quantization
21
+ # @return [Integer, nil] Maximum completion tokens
22
+ attr_reader :max_completion_tokens
23
+ # @return [Integer, nil] Maximum prompt tokens
24
+ attr_reader :max_prompt_tokens
25
+ # @return [Array<String>] Supported parameters
26
+ attr_reader :supported_parameters
27
+ # @return [String, nil] Endpoint status
28
+ attr_reader :status
29
+ # @return [Float, nil] Uptime in the last 30 minutes
30
+ attr_reader :uptime_last_30m
31
+ # @return [Boolean] Whether the endpoint supports implicit caching
32
+ attr_reader :supports_implicit_caching
33
+
34
+ # @param attributes [Hash] Raw attributes from OpenRouter API
35
+ def initialize(attributes)
36
+ @name = attributes["name"]
37
+ @model_name = attributes["model_name"]
38
+ @context_length = attributes["context_length"]
39
+ @pricing = attributes["pricing"]
40
+ @provider_name = attributes["provider_name"]
41
+ @tag = attributes["tag"]
42
+ @quantization = attributes["quantization"]
43
+ @max_completion_tokens = attributes["max_completion_tokens"]
44
+ @max_prompt_tokens = attributes["max_prompt_tokens"]
45
+ @supported_parameters = attributes["supported_parameters"] || []
46
+ @status = attributes["status"]
47
+ @uptime_last_30m = attributes["uptime_last_30m"]
48
+ @supports_implicit_caching = attributes["supports_implicit_caching"] || false
49
+ end
50
+
51
+ # Get the prompt/input price per token.
52
+ # @return [Float, nil]
53
+ def prompt_price
54
+ @pricing&.dig("prompt")&.to_f
55
+ end
56
+
57
+ # Get the completion/output price per token.
58
+ # @return [Float, nil]
59
+ def completion_price
60
+ @pricing&.dig("completion")&.to_f
61
+ end
62
+
63
+ # Get the image price.
64
+ # @return [Float, nil]
65
+ def image_price
66
+ @pricing&.dig("image")&.to_f
67
+ end
68
+
69
+ # Get any discount percentage.
70
+ # @return [Float, nil]
71
+ def discount
72
+ @pricing&.dig("discount")&.to_f
73
+ end
74
+
75
+ # Check if this endpoint is free.
76
+ # @return [Boolean]
77
+ def free?
78
+ prompt_price&.zero? && completion_price&.zero?
79
+ end
80
+
81
+ # Check if this endpoint is currently available.
82
+ # Status "0" indicates the endpoint is healthy.
83
+ # @return [Boolean]
84
+ def available?
85
+ @status == "0"
86
+ end
87
+
88
+ # Check if this endpoint supports a specific parameter.
89
+ # @param parameter [String, Symbol] The parameter to check
90
+ # @return [Boolean]
91
+ def supports?(parameter)
92
+ @supported_parameters.include?(parameter.to_s)
93
+ end
94
+ end
95
+ end
@@ -18,7 +18,7 @@ module OpenRouter
18
18
  attr_reader :pricing
19
19
  # @return [Integer, nil] Top provider information
20
20
  attr_reader :top_provider
21
- # @return [String, nil] Model architecture
21
+ # @return [Hash, nil] Model architecture
22
22
  attr_reader :architecture
23
23
  # @return [Array<String>, nil] Supported parameters
24
24
  attr_reader :supported_parameters
@@ -26,6 +26,8 @@ module OpenRouter
26
26
  attr_reader :per_request_limits
27
27
  # @return [String, nil] Created timestamp
28
28
  attr_reader :created
29
+ # @return [Array<OpenRouter::Endpoint>, nil] Available endpoints for this model
30
+ attr_reader :endpoints
29
31
 
30
32
  # @param attributes [Hash] Raw attributes from OpenRouter API
31
33
  # @param client [OpenRouter::Client] HTTP client
@@ -68,26 +70,32 @@ module OpenRouter
68
70
 
69
71
  class << self
70
72
  # Find a specific model by ID. Raises NotFoundError if not found.
71
- # @param id [String] The model identifier
73
+ # @param id [String] The model identifier (e.g., "openai/gpt-4")
72
74
  # @param client [OpenRouter::Client] HTTP client
73
75
  # @return [OpenRouter::Model]
74
76
  # @raise [OpenRouter::NotFoundError] if model not found
75
77
  def find(id, client: OpenRouter.client)
76
- model = find_by(id: id, client: client)
77
- raise NotFoundError, "Model not found: #{id}" unless model
78
+ author, slug = parse_model_id(id)
79
+ path = "#{MODELS_PATH}/#{author}/#{slug}/endpoints"
80
+
81
+ response = client.get(path)
82
+ return new(response["data"], client: client) if response && response["data"]
78
83
 
79
- model
84
+ raise NotFoundError, "Model not found: #{id}"
85
+ rescue NotFoundError
86
+ raise
87
+ rescue OpenRouter::Error
88
+ raise NotFoundError, "Model not found: #{id}"
80
89
  end
81
90
 
82
91
  # Find a specific model by ID. Returns nil if not found.
83
- # @param id [String] The model identifier
92
+ # @param id [String] The model identifier (e.g., "openai/gpt-4")
84
93
  # @param client [OpenRouter::Client] HTTP client
85
94
  # @return [OpenRouter::Model, nil]
86
95
  def find_by(id:, client: OpenRouter.client)
87
- response = client.get(MODELS_PATH)
88
- models = Array(response && response["data"])
89
- entry = models.find { |model| model["id"] == id }
90
- entry ? new(entry, client: client) : nil
96
+ find(id, client: client)
97
+ rescue NotFoundError
98
+ nil
91
99
  end
92
100
 
93
101
  # Find a specific model by ID. Raises NotFoundError if not found.
@@ -144,6 +152,19 @@ module OpenRouter
144
152
  def free(client: OpenRouter.client)
145
153
  all(client: client).select(&:free?)
146
154
  end
155
+
156
+ private
157
+
158
+ # Parse a model ID into author and slug components.
159
+ # @param id [String] The model identifier (e.g., "openai/gpt-4")
160
+ # @return [Array<String>] [author, slug]
161
+ # @raise [ArgumentError] if the ID format is invalid
162
+ def parse_model_id(id)
163
+ parts = id.to_s.split("/", 2)
164
+ raise ArgumentError, "Invalid model ID format: #{id}. Expected 'author/slug'" if parts.length != 2
165
+
166
+ parts
167
+ end
147
168
  end
148
169
 
149
170
  private
@@ -159,6 +180,13 @@ module OpenRouter
159
180
  @supported_parameters = attributes["supported_parameters"]
160
181
  @per_request_limits = attributes["per_request_limits"]
161
182
  @created = attributes["created"]
183
+ @endpoints = build_endpoints(attributes["endpoints"])
184
+ end
185
+
186
+ def build_endpoints(endpoints_data)
187
+ return nil unless endpoints_data
188
+
189
+ endpoints_data.map { |attrs| OpenRouter::Endpoint.new(attrs) }
162
190
  end
163
191
  end
164
192
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenRouter
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/openrouter.rb CHANGED
@@ -113,7 +113,9 @@ end
113
113
  require_relative "openrouter/client"
114
114
  require_relative "openrouter/completion"
115
115
  require_relative "openrouter/stream"
116
+ require_relative "openrouter/endpoint"
116
117
  require_relative "openrouter/model"
118
+ require_relative "openrouter/embedding"
117
119
  require_relative "openrouter/generation"
118
120
  require_relative "openrouter/credit"
119
121
  require_relative "openrouter/api_key"
@@ -0,0 +1,68 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module OpenRouter
5
+ class Embedding
6
+ EMBEDDINGS_PATH = T.let(T.unsafe(nil), String)
7
+ EMBEDDING_MODELS_PATH = T.let(T.unsafe(nil), String)
8
+
9
+ sig { returns(String) }
10
+ def model; end
11
+
12
+ sig { returns(T::Array[OpenRouter::EmbeddingData]) }
13
+ def data; end
14
+
15
+ sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
16
+ def usage; end
17
+
18
+ sig { params(attributes: T.untyped).void }
19
+ def initialize(attributes); end
20
+
21
+ sig do
22
+ params(
23
+ model: String,
24
+ input: T.any(String, T::Array[String]),
25
+ client: OpenRouter::Client,
26
+ options: T.untyped
27
+ ).returns(OpenRouter::Embedding)
28
+ end
29
+ def self.create!(model:, input:, client: OpenRouter.client, **options); end
30
+
31
+ sig { params(client: OpenRouter::Client).returns(T::Array[T::Hash[T.untyped, T.untyped]]) }
32
+ def self.models(client: OpenRouter.client); end
33
+
34
+ sig { returns(T.nilable(T::Array[Float])) }
35
+ def vector; end
36
+
37
+ sig { returns(T::Array[T::Array[Float]]) }
38
+ def vectors; end
39
+
40
+ sig { returns(T.nilable(Integer)) }
41
+ def dimensions; end
42
+
43
+ sig { returns(T.nilable(Integer)) }
44
+ def total_tokens; end
45
+
46
+ sig { returns(T.nilable(Integer)) }
47
+ def prompt_tokens; end
48
+
49
+ private
50
+
51
+ sig { params(data_array: T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])).returns(T::Array[OpenRouter::EmbeddingData]) }
52
+ def build_data(data_array); end
53
+ end
54
+
55
+ class EmbeddingData
56
+ sig { returns(Integer) }
57
+ def index; end
58
+
59
+ sig { returns(T::Array[Float]) }
60
+ def embedding; end
61
+
62
+ sig { params(attributes: T.untyped).void }
63
+ def initialize(attributes); end
64
+
65
+ sig { returns(Integer) }
66
+ def dimensions; end
67
+ end
68
+ end
@@ -0,0 +1,69 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module OpenRouter
5
+ class Endpoint
6
+ sig { returns(String) }
7
+ def name; end
8
+
9
+ sig { returns(String) }
10
+ def model_name; end
11
+
12
+ sig { returns(Integer) }
13
+ def context_length; end
14
+
15
+ sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
16
+ def pricing; end
17
+
18
+ sig { returns(String) }
19
+ def provider_name; end
20
+
21
+ sig { returns(T.nilable(String)) }
22
+ def tag; end
23
+
24
+ sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
25
+ def quantization; end
26
+
27
+ sig { returns(T.nilable(Integer)) }
28
+ def max_completion_tokens; end
29
+
30
+ sig { returns(T.nilable(Integer)) }
31
+ def max_prompt_tokens; end
32
+
33
+ sig { returns(T::Array[String]) }
34
+ def supported_parameters; end
35
+
36
+ sig { returns(T.nilable(String)) }
37
+ def status; end
38
+
39
+ sig { returns(T.nilable(Float)) }
40
+ def uptime_last_30m; end
41
+
42
+ sig { returns(T::Boolean) }
43
+ def supports_implicit_caching; end
44
+
45
+ sig { params(attributes: T.untyped).void }
46
+ def initialize(attributes); end
47
+
48
+ sig { returns(T.nilable(Float)) }
49
+ def prompt_price; end
50
+
51
+ sig { returns(T.nilable(Float)) }
52
+ def completion_price; end
53
+
54
+ sig { returns(T.nilable(Float)) }
55
+ def image_price; end
56
+
57
+ sig { returns(T.nilable(Float)) }
58
+ def discount; end
59
+
60
+ sig { returns(T::Boolean) }
61
+ def free?; end
62
+
63
+ sig { returns(T::Boolean) }
64
+ def available?; end
65
+
66
+ sig { params(parameter: T.any(String, Symbol)).returns(T::Boolean) }
67
+ def supports?(parameter); end
68
+ end
69
+ end
@@ -35,6 +35,9 @@ module OpenRouter
35
35
  sig { returns(T.nilable(String)) }
36
36
  def created; end
37
37
 
38
+ sig { returns(T.nilable(T::Array[OpenRouter::Endpoint])) }
39
+ def endpoints; end
40
+
38
41
  sig { params(attributes: T.untyped, client: OpenRouter::Client).void }
39
42
  def initialize(attributes, client: OpenRouter.client); end
40
43
 
@@ -82,11 +85,19 @@ module OpenRouter
82
85
 
83
86
  sig { params(client: OpenRouter::Client).returns(T::Array[OpenRouter::Model]) }
84
87
  def free(client: OpenRouter.client); end
88
+
89
+ private
90
+
91
+ sig { params(id: String).returns([String, String]) }
92
+ def parse_model_id(id); end
85
93
  end
86
94
 
87
95
  private
88
96
 
89
97
  sig { params(attributes: T.untyped).void }
90
98
  def reset_attributes(attributes); end
99
+
100
+ sig { params(endpoints_data: T.nilable(T::Array[T::Hash[T.untyped, T.untyped]])).returns(T.nilable(T::Array[OpenRouter::Endpoint])) }
101
+ def build_endpoints(endpoints_data); end
91
102
  end
92
103
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openrouter_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dylan Player
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-01-06 00:00:00.000000000 Z
10
+ date: 2026-01-07 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: faraday
@@ -44,6 +44,8 @@ files:
44
44
  - lib/openrouter/client.rb
45
45
  - lib/openrouter/completion.rb
46
46
  - lib/openrouter/credit.rb
47
+ - lib/openrouter/embedding.rb
48
+ - lib/openrouter/endpoint.rb
47
49
  - lib/openrouter/generation.rb
48
50
  - lib/openrouter/model.rb
49
51
  - lib/openrouter/stream.rb
@@ -53,6 +55,8 @@ files:
53
55
  - rbi/openrouter/client.rbi
54
56
  - rbi/openrouter/completion.rbi
55
57
  - rbi/openrouter/credit.rbi
58
+ - rbi/openrouter/embedding.rbi
59
+ - rbi/openrouter/endpoint.rbi
56
60
  - rbi/openrouter/generation.rbi
57
61
  - rbi/openrouter/model.rbi
58
62
  - rbi/openrouter/openrouter.rbi