fal-ai 0.1.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: 8c31c5366749b46a776ea9f3805c748ba27b8a1db046caa47ed16675313acc3b
4
+ data.tar.gz: ecbcc1de0c64dd4a9f11921b64c4a1cb8c5aa8fb30607b0990a80281976a990a
5
+ SHA512:
6
+ metadata.gz: e8ae00e9d1c02a4e69ea7448bfa1ddfa47e108a918259bed282e8e1e0c61ffe57a8000a39cc06c1943d5ea3fd45226d523bac465698028848d500ee294a5d1bd
7
+ data.tar.gz: c74109cc7c27b43fd987f39fa57f3a25ef6767e802b5333cbcb36591f5e77285e8e0b4de8361e7b449ba5581fa0181637ccb67bf2712cd095954db15ed34f737
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.0
3
+
4
+ Style/StringLiterals:
5
+ EnforcedStyle: double_quotes
6
+
7
+ Style/StringLiteralsInInterpolation:
8
+ EnforcedStyle: double_quotes
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2025-12-05
9
+
10
+ ### Added
11
+
12
+ - Initial release
13
+ - Synchronous `run` method for direct model inference
14
+ - Queue-based `subscribe` method with polling for long-running tasks
15
+ - Direct queue operations (`submit`, `status`, `result`)
16
+ - Support for all fal.ai models (Flux, Stable Diffusion, etc.)
17
+ - Configurable timeout and poll intervals
18
+ - Comprehensive error handling with typed exceptions
19
+ - Full test coverage
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Matt Culpepper
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # fal-ai
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/fal-ai.svg)](https://badge.fury.io/rb/fal-ai)
4
+
5
+ Ruby client for [fal.ai](https://fal.ai) - the generative AI platform with 600+ models.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem "fal-ai"
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ gem install fal-ai
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Configuration
30
+
31
+ ```ruby
32
+ require "fal-ai"
33
+
34
+ # Configure with API key (or set FAL_KEY environment variable)
35
+ Fal.configure do |config|
36
+ config.api_key = "your-api-key"
37
+ config.timeout = 300 # seconds (default: 300)
38
+ config.poll_interval = 0.5 # seconds (default: 0.5)
39
+ end
40
+ ```
41
+
42
+ ### Synchronous Run
43
+
44
+ For quick operations, use `run` to execute synchronously:
45
+
46
+ ```ruby
47
+ client = Fal.client
48
+
49
+ result = client.run("fal-ai/flux/dev", {
50
+ prompt: "a beautiful sunset over mountains",
51
+ image_size: "landscape_16_9"
52
+ })
53
+
54
+ puts result["images"].first["url"]
55
+ ```
56
+
57
+ ### Subscribe (Queue with Polling)
58
+
59
+ For longer operations, use `subscribe` to submit to the queue and poll until complete:
60
+
61
+ ```ruby
62
+ client = Fal.client
63
+
64
+ result = client.subscribe("fal-ai/flux/dev", { prompt: "a cat" }) do |status|
65
+ case status
66
+ when Fal::Status::Queued
67
+ puts "Queued at position #{status.position}"
68
+ when Fal::Status::InProgress
69
+ puts "Processing..."
70
+ end
71
+ end
72
+
73
+ puts "Completed! Image: #{result['images'].first['url']}"
74
+ ```
75
+
76
+ ### Direct Queue Operations
77
+
78
+ For more control, use queue operations directly:
79
+
80
+ ```ruby
81
+ client = Fal.client
82
+
83
+ # Submit to queue
84
+ request_id = client.queue.submit("fal-ai/flux/dev", {
85
+ prompt: "a dog playing fetch"
86
+ })
87
+
88
+ puts "Submitted: #{request_id}"
89
+
90
+ # Poll for status
91
+ loop do
92
+ status = client.queue.status("fal-ai/flux/dev", request_id)
93
+
94
+ if status.completed?
95
+ result = client.queue.result("fal-ai/flux/dev", request_id)
96
+ puts "Done! #{result['images'].first['url']}"
97
+ break
98
+ end
99
+
100
+ puts "Status: #{status.class.name}"
101
+ sleep 1
102
+ end
103
+ ```
104
+
105
+ ### Error Handling
106
+
107
+ ```ruby
108
+ begin
109
+ result = client.run("fal-ai/flux/dev", { prompt: "a cat" })
110
+ rescue Fal::AuthenticationError
111
+ puts "Check your API key"
112
+ rescue Fal::RateLimitError => e
113
+ puts "Rate limited. Status: #{e.status_code}"
114
+ rescue Fal::ApiError => e
115
+ puts "API error: #{e.message}"
116
+ rescue Fal::ConnectionError => e
117
+ puts "Network issue: #{e.original_error}"
118
+ rescue Fal::TimeoutError
119
+ puts "Request timed out"
120
+ end
121
+ ```
122
+
123
+ ## Development
124
+
125
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt.
126
+
127
+ ## Contributing
128
+
129
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mculp/fal-ai-ruby.
130
+
131
+ ## License
132
+
133
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/lib/fal/client.rb ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ # Main client facade providing public API.
5
+ #
6
+ # @example
7
+ # client = Fal.client
8
+ # result = client.run("fal-ai/flux/dev", { prompt: "a cat" })
9
+ class Client
10
+ def initialize(config:, connection: nil)
11
+ @config = config
12
+ @connection = connection || Connection.new(config: config)
13
+ end
14
+
15
+ def run(app_id, input)
16
+ endpoint = Endpoints::Run.new(app_id: app_id, base_url: @config.run_url)
17
+ response = @connection.post(endpoint, body: input)
18
+ response.data
19
+ end
20
+
21
+ def subscribe(app_id, input, &on_queue_update)
22
+ request_id = queue.submit(app_id, input)
23
+ subscriber.wait_for_completion(app_id, request_id, &on_queue_update)
24
+ end
25
+
26
+ def queue
27
+ @queue ||= Queue.new(connection: @connection, config: @config)
28
+ end
29
+
30
+ private
31
+
32
+ def subscriber
33
+ @subscriber ||= Subscriber.new(
34
+ queue: queue,
35
+ poll_interval: @config.poll_interval,
36
+ timeout: @config.timeout
37
+ )
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ # Holds configuration for the Fal client.
5
+ #
6
+ # @example
7
+ # Fal.configure do |config|
8
+ # config.api_key = "your-api-key"
9
+ # config.timeout = 300
10
+ # end
11
+ class Configuration
12
+ attr_accessor :timeout, :poll_interval
13
+ attr_writer :api_key
14
+
15
+ DEFAULT_TIMEOUT = 300
16
+ DEFAULT_POLL_INTERVAL = 0.5
17
+ RUN_HOST = "fal.run"
18
+ QUEUE_HOST = "queue.fal.run"
19
+
20
+ def initialize
21
+ @api_key = ENV.fetch("FAL_KEY", nil)
22
+ @timeout = DEFAULT_TIMEOUT
23
+ @poll_interval = DEFAULT_POLL_INTERVAL
24
+ end
25
+
26
+ def api_key
27
+ @api_key or raise ConfigurationError, "API key not configured. Set FAL_KEY or call Fal.configure"
28
+ end
29
+
30
+ def run_url
31
+ "https://#{RUN_HOST}"
32
+ end
33
+
34
+ def queue_url
35
+ "https://#{QUEUE_HOST}"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "http"
4
+
5
+ module Fal
6
+ # HTTP connection wrapper using http.rb gem.
7
+ # Dependency-injected for testability.
8
+ class Connection
9
+ def initialize(config:, http: HTTP)
10
+ @config = config
11
+ @http = http
12
+ @request = Request.new(config: config)
13
+ end
14
+
15
+ def post(endpoint, body: nil)
16
+ response = perform_post(endpoint, body)
17
+ handle_response(response)
18
+ end
19
+
20
+ def get(endpoint)
21
+ response = perform_get(endpoint)
22
+ handle_response(response)
23
+ end
24
+
25
+ private
26
+
27
+ def perform_post(endpoint, body)
28
+ @http
29
+ .headers(@request.headers)
30
+ .timeout(@config.timeout)
31
+ .post(endpoint.url, body: body ? @request.body(body) : nil)
32
+ rescue HTTP::Error => e
33
+ raise ConnectionError.new("HTTP request failed: #{e.message}", original_error: e)
34
+ end
35
+
36
+ def perform_get(endpoint)
37
+ @http
38
+ .headers(@request.headers)
39
+ .timeout(@config.timeout)
40
+ .get(endpoint.url)
41
+ rescue HTTP::Error => e
42
+ raise ConnectionError.new("HTTP request failed: #{e.message}", original_error: e)
43
+ end
44
+
45
+ def handle_response(http_response)
46
+ response = Response.new(http_response)
47
+ return response if response.success?
48
+
49
+ raise_api_error(response)
50
+ end
51
+
52
+ def raise_api_error(response)
53
+ error_class = error_class_for(response.status_code)
54
+ raise error_class.new(
55
+ response.error_message,
56
+ status_code: response.status_code,
57
+ response_body: response.data
58
+ )
59
+ end
60
+
61
+ def error_class_for(status_code)
62
+ case status_code
63
+ when 401 then AuthenticationError
64
+ when 404 then NotFoundError
65
+ when 429 then RateLimitError
66
+ when 500..599 then ServerError
67
+ else ApiError
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ module Endpoints
5
+ # Endpoint for synchronous run: POST https://fal.run/{app_id}
6
+ class Run
7
+ def initialize(app_id:, base_url:)
8
+ @app_id = app_id
9
+ @base_url = base_url
10
+ end
11
+
12
+ def url
13
+ "#{@base_url}/#{@app_id}"
14
+ end
15
+
16
+ def method
17
+ :post
18
+ end
19
+ end
20
+
21
+ # Endpoint for queue submit: POST https://queue.fal.run/{app_id}
22
+ class Submit
23
+ def initialize(app_id:, base_url:)
24
+ @app_id = app_id
25
+ @base_url = base_url
26
+ end
27
+
28
+ def url
29
+ "#{@base_url}/#{@app_id}"
30
+ end
31
+
32
+ def method
33
+ :post
34
+ end
35
+ end
36
+
37
+ # Endpoint for queue status: GET https://queue.fal.run/{app_id}/requests/{request_id}/status
38
+ class Status
39
+ def initialize(app_id:, request_id:, base_url:)
40
+ @app_id = app_id
41
+ @request_id = request_id
42
+ @base_url = base_url
43
+ end
44
+
45
+ def url
46
+ "#{@base_url}/#{@app_id}/requests/#{@request_id}/status"
47
+ end
48
+
49
+ def method
50
+ :get
51
+ end
52
+ end
53
+
54
+ # Endpoint for queue result: GET https://queue.fal.run/{app_id}/requests/{request_id}
55
+ class Result
56
+ def initialize(app_id:, request_id:, base_url:)
57
+ @app_id = app_id
58
+ @request_id = request_id
59
+ @base_url = base_url
60
+ end
61
+
62
+ def url
63
+ "#{@base_url}/#{@app_id}/requests/#{@request_id}"
64
+ end
65
+
66
+ def method
67
+ :get
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/fal/errors.rb ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ # Base error for all Fal errors
5
+ class Error < StandardError; end
6
+
7
+ # Raised when configuration is invalid or missing
8
+ class ConfigurationError < Error; end
9
+
10
+ # Raised when HTTP connection fails
11
+ class ConnectionError < Error
12
+ attr_reader :original_error
13
+
14
+ def initialize(message, original_error: nil)
15
+ super(message)
16
+ @original_error = original_error
17
+ end
18
+ end
19
+
20
+ # Base class for API errors (non-2xx responses)
21
+ class ApiError < Error
22
+ attr_reader :status_code, :response_body
23
+
24
+ def initialize(message, status_code:, response_body: nil)
25
+ super(message)
26
+ @status_code = status_code
27
+ @response_body = response_body
28
+ end
29
+ end
30
+
31
+ # 401 Unauthorized
32
+ class AuthenticationError < ApiError; end
33
+
34
+ # 404 Not Found
35
+ class NotFoundError < ApiError; end
36
+
37
+ # 429 Too Many Requests
38
+ class RateLimitError < ApiError; end
39
+
40
+ # 5xx Server Errors
41
+ class ServerError < ApiError; end
42
+
43
+ # Raised when polling times out
44
+ class TimeoutError < Error; end
45
+ end
data/lib/fal/queue.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ # Queue operations: submit, status, result.
5
+ class Queue
6
+ def initialize(connection:, config:)
7
+ @connection = connection
8
+ @config = config
9
+ end
10
+
11
+ def submit(app_id, input)
12
+ endpoint = Endpoints::Submit.new(app_id: app_id, base_url: @config.queue_url)
13
+ response = @connection.post(endpoint, body: input)
14
+ response.request_id
15
+ end
16
+
17
+ def status(app_id, request_id)
18
+ endpoint = Endpoints::Status.new(
19
+ app_id: app_id,
20
+ request_id: request_id,
21
+ base_url: @config.queue_url
22
+ )
23
+ response = @connection.get(endpoint)
24
+ response.to_status
25
+ end
26
+
27
+ def result(app_id, request_id)
28
+ endpoint = Endpoints::Result.new(
29
+ app_id: app_id,
30
+ request_id: request_id,
31
+ base_url: @config.queue_url
32
+ )
33
+ response = @connection.get(endpoint)
34
+ response.data
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Fal
6
+ # Builds HTTP request components (headers, body).
7
+ class Request
8
+ CONTENT_TYPE = "application/json"
9
+ USER_AGENT = "fal-ruby/#{VERSION}"
10
+
11
+ def initialize(config:)
12
+ @config = config
13
+ end
14
+
15
+ def headers
16
+ {
17
+ "Authorization" => "Key #{@config.api_key}",
18
+ "Content-Type" => CONTENT_TYPE,
19
+ "User-Agent" => USER_AGENT
20
+ }
21
+ end
22
+
23
+ def body(input)
24
+ JSON.generate(input)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Fal
6
+ # Parses HTTP responses and creates appropriate objects.
7
+ class Response
8
+ def initialize(http_response)
9
+ @http_response = http_response
10
+ end
11
+
12
+ def status_code
13
+ @http_response.status.to_i
14
+ end
15
+
16
+ def success?
17
+ status_code >= 200 && status_code < 300
18
+ end
19
+
20
+ def data
21
+ @data ||= parse_body
22
+ end
23
+
24
+ def request_id
25
+ data["request_id"]
26
+ end
27
+
28
+ def error_message
29
+ data["detail"] || data["message"] || "Unknown error"
30
+ end
31
+
32
+ def to_status
33
+ status_class.new(data)
34
+ end
35
+
36
+ private
37
+
38
+ def parse_body
39
+ JSON.parse(@http_response.body.to_s)
40
+ rescue JSON::ParserError
41
+ { "raw" => @http_response.body.to_s }
42
+ end
43
+
44
+ def status_class
45
+ case data["status"]
46
+ when "IN_QUEUE" then Status::Queued
47
+ when "IN_PROGRESS" then Status::InProgress
48
+ when "COMPLETED" then Status::Completed
49
+ else Status::Base
50
+ end
51
+ end
52
+ end
53
+ end
data/lib/fal/status.rb ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ module Status
5
+ # Base class for all status types
6
+ class Base
7
+ attr_reader :raw_data
8
+
9
+ def initialize(raw_data)
10
+ @raw_data = raw_data
11
+ end
12
+
13
+ def queued?
14
+ false
15
+ end
16
+
17
+ def in_progress?
18
+ false
19
+ end
20
+
21
+ def completed?
22
+ false
23
+ end
24
+ end
25
+
26
+ # Request is queued, waiting to be processed
27
+ class Queued < Base
28
+ def position
29
+ raw_data["queue_position"] || raw_data["position"]
30
+ end
31
+
32
+ def queued?
33
+ true
34
+ end
35
+ end
36
+
37
+ # Request is currently being processed
38
+ class InProgress < Base
39
+ def logs
40
+ raw_data["logs"] || []
41
+ end
42
+
43
+ def in_progress?
44
+ true
45
+ end
46
+ end
47
+
48
+ # Request has completed successfully
49
+ class Completed < Base
50
+ def logs
51
+ raw_data["logs"] || []
52
+ end
53
+
54
+ def metrics
55
+ raw_data["metrics"] || {}
56
+ end
57
+
58
+ def completed?
59
+ true
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ # Polls queue until completion, yielding status updates.
5
+ class Subscriber
6
+ def initialize(queue:, poll_interval:, timeout:)
7
+ @queue = queue
8
+ @poll_interval = poll_interval
9
+ @timeout = timeout
10
+ end
11
+
12
+ def wait_for_completion(app_id, request_id, &on_update)
13
+ deadline = Time.now + @timeout
14
+
15
+ loop do
16
+ check_timeout(deadline)
17
+ status = @queue.status(app_id, request_id)
18
+ yield_status(status, &on_update)
19
+ return fetch_result(app_id, request_id) if status.completed?
20
+
21
+ sleep(@poll_interval)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def check_timeout(deadline)
28
+ return if Time.now < deadline
29
+
30
+ raise TimeoutError, "Polling timed out after #{@timeout} seconds"
31
+ end
32
+
33
+ def yield_status(status, &on_update)
34
+ on_update&.call(status)
35
+ end
36
+
37
+ def fetch_result(app_id, request_id)
38
+ @queue.result(app_id, request_id)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fal
4
+ VERSION = "0.1.0"
5
+ end
data/lib/fal-ai.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Entry point for `require "fal-ai"`
4
+ # Delegates to the main Fal module
5
+ require_relative "fal"
data/lib/fal.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "fal/version"
4
+ require_relative "fal/errors"
5
+ require_relative "fal/configuration"
6
+ require_relative "fal/endpoints"
7
+ require_relative "fal/status"
8
+ require_relative "fal/request"
9
+ require_relative "fal/response"
10
+ require_relative "fal/connection"
11
+ require_relative "fal/queue"
12
+ require_relative "fal/subscriber"
13
+ require_relative "fal/client"
14
+
15
+ # Ruby client for fal.ai Model APIs
16
+ #
17
+ # @example
18
+ # Fal.configure do |config|
19
+ # config.api_key = "your-api-key"
20
+ # end
21
+ #
22
+ # client = Fal.client
23
+ # result = client.run("fal-ai/flux/dev", { prompt: "a cat" })
24
+ module Fal
25
+ class << self
26
+ def configure
27
+ yield(configuration)
28
+ end
29
+
30
+ def configuration
31
+ @configuration ||= Configuration.new
32
+ end
33
+
34
+ def client(config: configuration)
35
+ Client.new(config: config)
36
+ end
37
+
38
+ def reset_configuration!
39
+ @configuration = nil
40
+ end
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fal-ai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Culpepper
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: http
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.0'
26
+ description: A Ruby client library for fal.ai's generative AI platform. Run inference
27
+ on 600+ AI models including Flux, Stable Diffusion, and more with synchronous and
28
+ queue-based APIs.
29
+ email:
30
+ - matt@culpepper.co
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rspec"
36
+ - ".rubocop.yml"
37
+ - ".ruby-version"
38
+ - CHANGELOG.md
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - lib/fal-ai.rb
43
+ - lib/fal.rb
44
+ - lib/fal/client.rb
45
+ - lib/fal/configuration.rb
46
+ - lib/fal/connection.rb
47
+ - lib/fal/endpoints.rb
48
+ - lib/fal/errors.rb
49
+ - lib/fal/queue.rb
50
+ - lib/fal/request.rb
51
+ - lib/fal/response.rb
52
+ - lib/fal/status.rb
53
+ - lib/fal/subscriber.rb
54
+ - lib/fal/version.rb
55
+ homepage: https://github.com/mculp/fal-ai-ruby
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ homepage_uri: https://fal.ai
60
+ source_code_uri: https://github.com/mculp/fal-ai-ruby
61
+ changelog_uri: https://github.com/mculp/fal-ai-ruby/blob/main/CHANGELOG.md
62
+ documentation_uri: https://rubydoc.info/gems/fal-ai
63
+ bug_tracker_uri: https://github.com/mculp/fal-ai-ruby/issues
64
+ rubygems_mfa_required: 'true'
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.0.0
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 4.0.0
80
+ specification_version: 4
81
+ summary: Ruby client for fal.ai generative AI platform
82
+ test_files: []