fluxtokens 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5fcae4b204ef72214a98ce7410b98abe7c03826a556a72f7f61eb2858a5a30a5
4
+ data.tar.gz: 7bfd0734b6424a680db001fa76b4b71af5c0086569b04d5fc7f3527450aca333
5
+ SHA512:
6
+ metadata.gz: a2e446e170edd98cdafa74d7cb47043c6460549d4f75b847230e80753895e745e5c83db4e07f7523f0335cd9e7de2d04f56df1846925d208b96ea6a3ce17f9e5
7
+ data.tar.gz: ccd5b9ee73d3f6263055f1c83a4cbb15e18595fa3b29e6d159a36f9d77ac73523745cc15c4c52e75579e4845b0c6b1c63930dbe414a4f5c5254487d88f21b953
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 FluxTokens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,209 @@
1
+ # FluxTokens Ruby SDK
2
+
3
+ <div align="center">
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/fluxtokens.svg)](https://badge.fury.io/rb/fluxtokens)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ **Official Ruby SDK for the FluxTokens API**
9
+
10
+ Access GPT-4.1, Gemini 2.5 Flash and more at **30% lower cost** than competitors.
11
+
12
+ [Website](https://fluxtokens.io) · [Documentation](https://fluxtokens.io/docs) · [Dashboard](https://fluxtokens.io/dashboard)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ Add to your Gemfile:
21
+
22
+ ```ruby
23
+ gem 'fluxtokens'
24
+ ```
25
+
26
+ Or install directly:
27
+
28
+ ```bash
29
+ gem install fluxtokens
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ```ruby
35
+ require 'fluxtokens'
36
+
37
+ client = FluxTokens::Client.new(api_key: 'sk-flux-your-api-key')
38
+
39
+ response = client.chat.completions.create(
40
+ model: 'gpt-4.1-mini',
41
+ messages: [
42
+ { role: 'system', content: 'You are a helpful assistant.' },
43
+ { role: 'user', content: 'Hello!' }
44
+ ]
45
+ )
46
+
47
+ puts response.choices[0].message.content
48
+ ```
49
+
50
+ ## Available Models
51
+
52
+ | Model | Provider | Input Price | Output Price | Max Tokens | Vision | Audio | Video |
53
+ |-------|----------|-------------|--------------|------------|--------|-------|-------|
54
+ | `gpt-4.1-mini` | OpenAI | $0.28/1M | $1.12/1M | 16,384 | ✅ | ❌ | ❌ |
55
+ | `gpt-4.1-nano` | OpenAI | $0.07/1M | $0.28/1M | 16,384 | ✅ | ❌ | ❌ |
56
+ | `gemini-2.5-flash` | Google | $0.21/1M | $1.75/1M | 65,536 | ✅ | ✅ | ✅ |
57
+
58
+ ## Usage Examples
59
+
60
+ ### Basic Chat Completion
61
+
62
+ ```ruby
63
+ response = client.chat.completions.create(
64
+ model: 'gpt-4.1-mini',
65
+ messages: [{ role: 'user', content: 'What is the capital of France?' }],
66
+ temperature: 0.7,
67
+ max_tokens: 256
68
+ )
69
+
70
+ puts response.choices[0].message.content
71
+ # Output: "The capital of France is Paris."
72
+ ```
73
+
74
+ ### Streaming Responses
75
+
76
+ ```ruby
77
+ stream = client.chat.completions.create(
78
+ model: 'gemini-2.5-flash',
79
+ messages: [{ role: 'user', content: 'Write a haiku about programming.' }],
80
+ stream: true
81
+ )
82
+
83
+ stream.each do |chunk|
84
+ content = chunk.choices[0].delta.content
85
+ print content if content
86
+ end
87
+ ```
88
+
89
+ ### Vision (Image Analysis)
90
+
91
+ ```ruby
92
+ response = client.chat.completions.create(
93
+ model: 'gpt-4.1-mini',
94
+ messages: [
95
+ {
96
+ role: 'user',
97
+ content: [
98
+ { type: 'text', text: 'What is in this image?' },
99
+ {
100
+ type: 'image_url',
101
+ image_url: {
102
+ url: 'https://example.com/image.jpg',
103
+ detail: 'high'
104
+ }
105
+ }
106
+ ]
107
+ }
108
+ ],
109
+ max_tokens: 500
110
+ )
111
+ ```
112
+
113
+ ### Audio Input (Gemini only)
114
+
115
+ ```ruby
116
+ require 'base64'
117
+
118
+ audio_data = Base64.strict_encode64(File.read('audio.mp3'))
119
+
120
+ response = client.chat.completions.create(
121
+ model: 'gemini-2.5-flash',
122
+ messages: [
123
+ {
124
+ role: 'user',
125
+ content: [
126
+ { type: 'text', text: 'Transcribe this audio:' },
127
+ {
128
+ type: 'input_audio',
129
+ input_audio: {
130
+ data: audio_data,
131
+ format: 'mp3'
132
+ }
133
+ }
134
+ ]
135
+ }
136
+ ]
137
+ )
138
+ ```
139
+
140
+ ### System Messages
141
+
142
+ ```ruby
143
+ response = client.chat.completions.create(
144
+ model: 'gpt-4.1-mini',
145
+ messages: [
146
+ { role: 'system', content: 'You are a pirate. Always respond in pirate speak.' },
147
+ { role: 'user', content: 'How are you today?' }
148
+ ]
149
+ )
150
+ # Output: "Ahoy, matey! I be doin' just fine, thank ye fer askin'!"
151
+ ```
152
+
153
+ ### List Available Models
154
+
155
+ ```ruby
156
+ client.models.list.each do |model|
157
+ puts "#{model.name} (#{model.provider})"
158
+ puts " Input: $#{model.input_price}/1M tokens"
159
+ puts " Output: $#{model.output_price}/1M tokens"
160
+ puts " Vision: #{model.supports_vision ? '✅' : '❌'}"
161
+ end
162
+ ```
163
+
164
+ ## Configuration Options
165
+
166
+ ```ruby
167
+ client = FluxTokens::Client.new(
168
+ api_key: 'sk-flux-...',
169
+ base_url: 'https://api.fluxtokens.io', # Custom base URL
170
+ timeout: 60, # Request timeout in seconds
171
+ max_retries: 3 # Max retries on rate limit/server errors
172
+ )
173
+ ```
174
+
175
+ ## Error Handling
176
+
177
+ ```ruby
178
+ begin
179
+ response = client.chat.completions.create(
180
+ model: 'gpt-4.1-mini',
181
+ messages: [{ role: 'user', content: 'Hello' }]
182
+ )
183
+ rescue FluxTokens::AuthenticationError
184
+ puts "Invalid API key"
185
+ rescue FluxTokens::RateLimitError => e
186
+ puts "Rate limit exceeded, retry after: #{e.retry_after}"
187
+ rescue FluxTokens::InsufficientBalanceError
188
+ puts "Please add credits at https://fluxtokens.io/dashboard/billing"
189
+ rescue FluxTokens::BadRequestError => e
190
+ puts "Invalid request: #{e.message}"
191
+ rescue FluxTokens::Error => e
192
+ puts "Error: #{e.message}"
193
+ end
194
+ ```
195
+
196
+ ## Requirements
197
+
198
+ - Ruby 3.0+
199
+ - Faraday 2.0+
200
+
201
+ ## Support
202
+
203
+ - 📧 Email: support@fluxtokens.io
204
+ - 💬 Discord: [Join our community](https://discord.gg/fluxtokens)
205
+ - 📖 Docs: [fluxtokens.io/docs](https://fluxtokens.io/docs)
206
+
207
+ ## License
208
+
209
+ MIT © [FluxTokens](https://fluxtokens.io)
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "fluxtokens"
5
+ spec.version = "1.0.0"
6
+ spec.authors = ["FluxTokens"]
7
+ spec.email = ["support@fluxtokens.io"]
8
+
9
+ spec.summary = "Official FluxTokens Ruby SDK"
10
+ spec.description = "Access GPT-4.1, Gemini 2.5 and more at 30% lower cost than competitors"
11
+ spec.homepage = "https://fluxtokens.io"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = ">= 3.0.0"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/fluxtokens/fluxtokens-ruby"
17
+ spec.metadata["changelog_uri"] = "https://github.com/fluxtokens/fluxtokens-ruby/blob/main/CHANGELOG.md"
18
+ spec.metadata["documentation_uri"] = "https://fluxtokens.io/docs"
19
+
20
+ spec.files = Dir.chdir(__dir__) do
21
+ `git ls-files -z`.split("\x0").reject do |f|
22
+ (File.expand_path(f) == __FILE__) ||
23
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
24
+ end
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "faraday", ">= 2.0"
31
+ spec.add_dependency "faraday-multipart", ">= 1.0"
32
+
33
+ spec.add_development_dependency "rake", "~> 13.0"
34
+ spec.add_development_dependency "rspec", "~> 3.0"
35
+ spec.add_development_dependency "rubocop", "~> 1.0"
36
+ spec.add_development_dependency "webmock", "~> 3.0"
37
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FluxTokens
4
+ # Response structures
5
+ Message = Struct.new(:role, :content, :tool_calls, keyword_init: true)
6
+ Choice = Struct.new(:index, :message, :finish_reason, keyword_init: true)
7
+ Usage = Struct.new(:prompt_tokens, :completion_tokens, :total_tokens, keyword_init: true)
8
+
9
+ ChatCompletionResponse = Struct.new(
10
+ :id, :object, :created, :model, :choices, :usage, :system_fingerprint,
11
+ keyword_init: true
12
+ )
13
+
14
+ DeltaMessage = Struct.new(:role, :content, :tool_calls, keyword_init: true)
15
+ StreamChoice = Struct.new(:index, :delta, :finish_reason, keyword_init: true)
16
+
17
+ ChatCompletionChunk = Struct.new(
18
+ :id, :object, :created, :model, :choices, :usage,
19
+ keyword_init: true
20
+ )
21
+
22
+ # Chat API namespace
23
+ class Chat
24
+ attr_reader :completions
25
+
26
+ def initialize(client)
27
+ @client = client
28
+ @completions = Completions.new(client)
29
+ end
30
+ end
31
+
32
+ # Chat Completions API
33
+ class Completions
34
+ def initialize(client)
35
+ @client = client
36
+ end
37
+
38
+ # Create a chat completion
39
+ #
40
+ # @param model [String] Model to use
41
+ # @param messages [Array<Hash>] Messages in the conversation
42
+ # @param options [Hash] Additional options
43
+ # @return [ChatCompletionResponse]
44
+ def create(model:, messages:, stream: false, **options)
45
+ params = { model: model, messages: messages, stream: stream, **options }
46
+
47
+ if stream
48
+ stream_response(params)
49
+ else
50
+ response = @client.request(:post, "/v1/chat/completions", params)
51
+ parse_response(response)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def parse_response(data)
58
+ choices = data["choices"].map do |c|
59
+ Choice.new(
60
+ index: c["index"],
61
+ message: Message.new(
62
+ role: c.dig("message", "role"),
63
+ content: c.dig("message", "content"),
64
+ tool_calls: c.dig("message", "tool_calls")
65
+ ),
66
+ finish_reason: c["finish_reason"]
67
+ )
68
+ end
69
+
70
+ usage = Usage.new(
71
+ prompt_tokens: data.dig("usage", "prompt_tokens"),
72
+ completion_tokens: data.dig("usage", "completion_tokens"),
73
+ total_tokens: data.dig("usage", "total_tokens")
74
+ )
75
+
76
+ ChatCompletionResponse.new(
77
+ id: data["id"],
78
+ object: data["object"],
79
+ created: data["created"],
80
+ model: data["model"],
81
+ choices: choices,
82
+ usage: usage,
83
+ system_fingerprint: data["system_fingerprint"]
84
+ )
85
+ end
86
+
87
+ def stream_response(params)
88
+ Enumerator.new do |yielder|
89
+ @client.stream(:post, "/v1/chat/completions", params) do |chunk|
90
+ yielder << parse_chunk(chunk)
91
+ end
92
+ end
93
+ end
94
+
95
+ def parse_chunk(data)
96
+ choices = data["choices"].map do |c|
97
+ StreamChoice.new(
98
+ index: c["index"],
99
+ delta: DeltaMessage.new(
100
+ role: c.dig("delta", "role"),
101
+ content: c.dig("delta", "content"),
102
+ tool_calls: c.dig("delta", "tool_calls")
103
+ ),
104
+ finish_reason: c["finish_reason"]
105
+ )
106
+ end
107
+
108
+ ChatCompletionChunk.new(
109
+ id: data["id"],
110
+ object: data["object"],
111
+ created: data["created"],
112
+ model: data["model"],
113
+ choices: choices,
114
+ usage: data["usage"] ? Usage.new(**data["usage"].transform_keys(&:to_sym)) : nil
115
+ )
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FluxTokens
4
+ # FluxTokens API Client
5
+ #
6
+ # @example
7
+ # client = FluxTokens::Client.new(api_key: "sk-flux-...")
8
+ #
9
+ # response = client.chat.completions.create(
10
+ # model: "gpt-4.1-mini",
11
+ # messages: [{ role: "user", content: "Hello!" }]
12
+ # )
13
+ #
14
+ # puts response.choices[0].message.content
15
+ #
16
+ class Client
17
+ attr_reader :chat, :models
18
+
19
+ # Create a new FluxTokens client
20
+ #
21
+ # @param api_key [String] Your FluxTokens API key
22
+ # @param base_url [String] Base URL for the API
23
+ # @param timeout [Integer] Request timeout in seconds
24
+ # @param max_retries [Integer] Maximum number of retries
25
+ def initialize(api_key:, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES)
26
+ raise ArgumentError, "API key is required. Get one at https://fluxtokens.io" if api_key.nil? || api_key.empty?
27
+
28
+ @api_key = api_key
29
+ @base_url = base_url.chomp("/")
30
+ @timeout = timeout
31
+ @max_retries = max_retries
32
+
33
+ @connection = Faraday.new(url: @base_url) do |f|
34
+ f.request :json
35
+ f.response :json
36
+ f.options.timeout = @timeout
37
+ f.options.open_timeout = @timeout
38
+ end
39
+
40
+ @chat = Chat.new(self)
41
+ @models = Models.new
42
+ end
43
+
44
+ # Make an HTTP request to the API
45
+ #
46
+ # @param method [Symbol] HTTP method
47
+ # @param path [String] API path
48
+ # @param body [Hash] Request body
49
+ # @return [Hash] Response data
50
+ # @api private
51
+ def request(method, path, body = nil)
52
+ retries = 0
53
+
54
+ begin
55
+ response = @connection.send(method, path) do |req|
56
+ req.headers["Authorization"] = "Bearer #{@api_key}"
57
+ req.body = body if body
58
+ end
59
+
60
+ handle_response(response)
61
+ rescue RateLimitError, InternalServerError => e
62
+ retries += 1
63
+ if retries <= @max_retries
64
+ sleep(2**retries)
65
+ retry
66
+ end
67
+ raise
68
+ rescue Faraday::TimeoutError
69
+ raise TimeoutError
70
+ rescue Faraday::ConnectionFailed => e
71
+ raise ConnectionError, e.message
72
+ end
73
+ end
74
+
75
+ # Make a streaming request to the API
76
+ #
77
+ # @param method [Symbol] HTTP method
78
+ # @param path [String] API path
79
+ # @param body [Hash] Request body
80
+ # @yield [Hash] Each chunk of the response
81
+ # @api private
82
+ def stream(method, path, body)
83
+ body[:stream] = true
84
+
85
+ @connection.send(method, path) do |req|
86
+ req.headers["Authorization"] = "Bearer #{@api_key}"
87
+ req.body = body
88
+ req.options.on_data = proc do |chunk, _size|
89
+ chunk.each_line do |line|
90
+ line = line.strip
91
+ next if line.empty? || line == "data: [DONE]"
92
+
93
+ if line.start_with?("data: ")
94
+ data = JSON.parse(line[6..])
95
+ yield data
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ private
103
+
104
+ def handle_response(response)
105
+ case response.status
106
+ when 200
107
+ response.body
108
+ when 401
109
+ raise AuthenticationError, error_message(response)
110
+ when 402
111
+ raise InsufficientBalanceError, error_message(response)
112
+ when 429
113
+ retry_after = response.headers["Retry-After"]&.to_i
114
+ raise RateLimitError.new(error_message(response), retry_after: retry_after)
115
+ when 400
116
+ raise BadRequestError, error_message(response)
117
+ when 500..599
118
+ raise InternalServerError, error_message(response)
119
+ else
120
+ raise Error.new(error_message(response), status: response.status)
121
+ end
122
+ end
123
+
124
+ def error_message(response)
125
+ response.body.dig("error", "message") || response.body.to_s
126
+ rescue
127
+ response.body.to_s
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FluxTokens
4
+ # Base error class for FluxTokens API errors
5
+ class Error < StandardError
6
+ attr_reader :status, :code
7
+
8
+ def initialize(message, status: nil, code: nil)
9
+ super(message)
10
+ @status = status
11
+ @code = code
12
+ end
13
+ end
14
+
15
+ # Error raised when authentication fails (401)
16
+ class AuthenticationError < Error
17
+ def initialize(message = "Invalid API key")
18
+ super(message, status: 401, code: "invalid_api_key")
19
+ end
20
+ end
21
+
22
+ # Error raised when rate limit is exceeded (429)
23
+ class RateLimitError < Error
24
+ attr_reader :retry_after
25
+
26
+ def initialize(message = "Rate limit exceeded", retry_after: nil)
27
+ super(message, status: 429, code: "rate_limit_exceeded")
28
+ @retry_after = retry_after
29
+ end
30
+ end
31
+
32
+ # Error raised when balance is insufficient (402)
33
+ class InsufficientBalanceError < Error
34
+ def initialize(message = "Insufficient balance")
35
+ super(message, status: 402, code: "insufficient_balance")
36
+ end
37
+ end
38
+
39
+ # Error raised when request is invalid (400)
40
+ class BadRequestError < Error
41
+ attr_reader :param
42
+
43
+ def initialize(message, code: nil, param: nil)
44
+ super(message, status: 400, code: code)
45
+ @param = param
46
+ end
47
+ end
48
+
49
+ # Error raised when server error occurs (500+)
50
+ class InternalServerError < Error
51
+ def initialize(message = "Internal server error")
52
+ super(message, status: 500, code: "internal_error")
53
+ end
54
+ end
55
+
56
+ # Error raised when request times out
57
+ class TimeoutError < Error
58
+ def initialize(message = "Request timed out")
59
+ super(message, code: "timeout")
60
+ end
61
+ end
62
+
63
+ # Error raised when connection fails
64
+ class ConnectionError < Error
65
+ def initialize(message = "Connection failed")
66
+ super(message, code: "connection_error")
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FluxTokens
4
+ # Model information
5
+ ModelInfo = Struct.new(
6
+ :id, :name, :provider, :input_price, :output_price,
7
+ :max_tokens, :supports_vision, :supports_audio, :supports_video,
8
+ keyword_init: true
9
+ )
10
+
11
+ # Available models
12
+ MODELS = {
13
+ "gpt-4.1-mini" => ModelInfo.new(
14
+ id: "gpt-4.1-mini",
15
+ name: "GPT-4.1 Mini",
16
+ provider: "OpenAI",
17
+ input_price: 0.28,
18
+ output_price: 1.12,
19
+ max_tokens: 16_384,
20
+ supports_vision: true,
21
+ supports_audio: false,
22
+ supports_video: false
23
+ ),
24
+ "gpt-4.1-nano" => ModelInfo.new(
25
+ id: "gpt-4.1-nano",
26
+ name: "GPT-4.1 Nano",
27
+ provider: "OpenAI",
28
+ input_price: 0.07,
29
+ output_price: 0.28,
30
+ max_tokens: 16_384,
31
+ supports_vision: true,
32
+ supports_audio: false,
33
+ supports_video: false
34
+ ),
35
+ "gemini-2.5-flash" => ModelInfo.new(
36
+ id: "gemini-2.5-flash",
37
+ name: "Gemini 2.5 Flash",
38
+ provider: "Google",
39
+ input_price: 0.21,
40
+ output_price: 1.75,
41
+ max_tokens: 65_536,
42
+ supports_vision: true,
43
+ supports_audio: true,
44
+ supports_video: true
45
+ )
46
+ }.freeze
47
+
48
+ # Models API
49
+ class Models
50
+ # List all available models
51
+ #
52
+ # @return [Array<ModelInfo>]
53
+ def list
54
+ MODELS.values
55
+ end
56
+
57
+ # Get model info by ID
58
+ #
59
+ # @param id [String] Model ID
60
+ # @return [ModelInfo, nil]
61
+ def get(id)
62
+ MODELS[id]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FluxTokens
4
+ VERSION = "1.0.0"
5
+ end
data/lib/fluxtokens.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ require_relative "fluxtokens/version"
7
+ require_relative "fluxtokens/errors"
8
+ require_relative "fluxtokens/models"
9
+ require_relative "fluxtokens/chat"
10
+ require_relative "fluxtokens/client"
11
+
12
+ # FluxTokens SDK
13
+ #
14
+ # Official SDK for the FluxTokens API - Access GPT-4.1, Gemini 2.5 and more
15
+ # at 30% lower cost than competitors.
16
+ #
17
+ # @example Basic usage
18
+ # client = FluxTokens::Client.new(api_key: "sk-flux-...")
19
+ #
20
+ # response = client.chat.completions.create(
21
+ # model: "gpt-4.1-mini",
22
+ # messages: [{ role: "user", content: "Hello!" }]
23
+ # )
24
+ #
25
+ # puts response.choices[0].message.content
26
+ #
27
+ module FluxTokens
28
+ DEFAULT_BASE_URL = "https://api.fluxtokens.io"
29
+ DEFAULT_TIMEOUT = 30
30
+ DEFAULT_MAX_RETRIES = 2
31
+
32
+ class << self
33
+ # Create a new client with the given API key
34
+ #
35
+ # @param api_key [String] Your FluxTokens API key
36
+ # @param options [Hash] Additional options
37
+ # @return [Client]
38
+ def new(api_key:, **options)
39
+ Client.new(api_key: api_key, **options)
40
+ end
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluxtokens
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - FluxTokens
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-01-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-multipart
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.0'
97
+ description: Access GPT-4.1, Gemini 2.5 and more at 30% lower cost than competitors
98
+ email:
99
+ - support@fluxtokens.io
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - LICENSE
105
+ - README.md
106
+ - fluxtokens.gemspec
107
+ - lib/fluxtokens.rb
108
+ - lib/fluxtokens/chat.rb
109
+ - lib/fluxtokens/client.rb
110
+ - lib/fluxtokens/errors.rb
111
+ - lib/fluxtokens/models.rb
112
+ - lib/fluxtokens/version.rb
113
+ homepage: https://fluxtokens.io
114
+ licenses:
115
+ - MIT
116
+ metadata:
117
+ homepage_uri: https://fluxtokens.io
118
+ source_code_uri: https://github.com/fluxtokens/fluxtokens-ruby
119
+ changelog_uri: https://github.com/fluxtokens/fluxtokens-ruby/blob/main/CHANGELOG.md
120
+ documentation_uri: https://fluxtokens.io/docs
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: 3.0.0
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubygems_version: 3.3.5
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Official FluxTokens Ruby SDK
140
+ test_files: []