roseflow-stabilityai 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.standard.yml +3 -0
  4. data/CHANGELOG.md +4 -0
  5. data/CODE_OF_CONDUCT.md +7 -0
  6. data/Gemfile +13 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +47 -0
  9. data/Rakefile +10 -0
  10. data/lib/roseflow/ai/models/stabilityai_adapter.rb +42 -0
  11. data/lib/roseflow/stabilityai/client.rb +78 -0
  12. data/lib/roseflow/stabilityai/config.rb +18 -0
  13. data/lib/roseflow/stabilityai/model.rb +39 -0
  14. data/lib/roseflow/stabilityai/model_repository.rb +32 -0
  15. data/lib/roseflow/stabilityai/operation_handler.rb +36 -0
  16. data/lib/roseflow/stabilityai/operations/base.rb +25 -0
  17. data/lib/roseflow/stabilityai/operations/image_to_image.rb +50 -0
  18. data/lib/roseflow/stabilityai/operations/masking.rb +54 -0
  19. data/lib/roseflow/stabilityai/operations/text_to_image.rb +37 -0
  20. data/lib/roseflow/stabilityai/operations/upscale.rb +77 -0
  21. data/lib/roseflow/stabilityai/provider.rb +27 -0
  22. data/lib/roseflow/stabilityai/response_handler.rb +57 -0
  23. data/lib/roseflow/stabilityai/responses/base_response.rb +17 -0
  24. data/lib/roseflow/stabilityai/responses/error_response.rb +24 -0
  25. data/lib/roseflow/stabilityai/responses/image_to_image_response.rb +21 -0
  26. data/lib/roseflow/stabilityai/responses/masking_response.rb +21 -0
  27. data/lib/roseflow/stabilityai/responses/text_to_image_response.rb +21 -0
  28. data/lib/roseflow/stabilityai/responses/upscale_response.rb +21 -0
  29. data/lib/roseflow/stabilityai/version.rb +18 -0
  30. data/lib/roseflow/stabilityai.rb +16 -0
  31. data/roseflow-stabilityai.gemspec +46 -0
  32. data/sig/roseflow/stabilityai.rbs +6 -0
  33. metadata +259 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 52d0eb1ead98627dee65b61a5af00bb0f705ef83b1744c7db3e144f2649a16ed
4
+ data.tar.gz: eab68fcb891590911d69d3fb3e2f1ec12f9bb0ae36f3b196d7ec9527b68f9fed
5
+ SHA512:
6
+ metadata.gz: 85894910204e152bb7ed3cd3179777b7e9e6d5a604a53fca7692893b1a3f4bb5048efeb094173b496a0a8be196a63bfc220edcabf030364ceba1493d8a8cd01a
7
+ data.tar.gz: e124b655082293c97b034512023e2061189cdcd9f90a5686b4a2fcbc22a27ded8f2da1862615d5cb4b81b845c22ffaea0a730b6ea9c5332cedd21ae3a40c1d42
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/testdouble/standard
3
+ ruby_version: 2.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ ## [0.1.0] - 2023-08-09
2
+
3
+ - Initial release
4
+ - Implements text-to-image, image-to-image, SDX4 upscaling, and masking
@@ -0,0 +1,7 @@
1
+ # Contributor Code of Conduct
2
+
3
+ The Roseflow team is committed to fostering a welcoming community.
4
+
5
+ **Our Code of Conduct can be found here**:
6
+
7
+ https://roseflow.ai/conduct
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in roseflow-stabilityai.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "rspec", "~> 3.0"
10
+ gem "standard", "~> 1.3"
11
+ gem "ulid-ruby"
12
+
13
+ eval_gemfile "Gemfile.local" if File.exist?("Gemfile.local")
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Lauri Jutila
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,47 @@
1
+ # Roseflow Stability-AI integration
2
+
3
+ This gem adds Stability-AI API support and integration for Roseflow, a framework for interacting with AI in Ruby.
4
+
5
+ ## Prerequisites
6
+
7
+ To use this gem effectively, you need the [core Roseflow gem](https://github.com/roseflow-ai/roseflow).
8
+
9
+ ## Installation
10
+
11
+ Install the gem and add to the application's Gemfile by executing:
12
+
13
+ $ bundle add roseflow-stabilityai
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ $ gem install roseflow-stabilityai
18
+
19
+ ## Usage
20
+
21
+ See full documentation how to configure and use Roseflow with StabilityAI at [docs.roseflow.ai](https://docs.roseflow.ai/stabilityai).
22
+
23
+ ## Contributing
24
+
25
+ Bug reports and pull requests are welcome on GitHub at https://github.com/roseflow-ai/roseflow-stabilityai.
26
+
27
+ ## Community
28
+
29
+ ### Discord
30
+
31
+ Join us in our [Discord](https://discord.gg/roseflow).
32
+
33
+ ### Twitter
34
+
35
+ Connect with the core team on Twitter.
36
+
37
+ <a href="https://twitter.com/ljuti" target="_blank">
38
+ <img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/ljuti?logo=twitter&style=social">
39
+ </a>
40
+
41
+ ## License
42
+
43
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
44
+
45
+ ## Code of Conduct
46
+
47
+ Everyone interacting in the Roseflow OpenAI project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/roseflow-ai/roseflow-stabilityai/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
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 "standard/rake"
9
+
10
+ task default: %i[spec standard]
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/ai/model_interface"
4
+ require "roseflow/ai/models/base_adapter"
5
+
6
+ module Roseflow
7
+ module AI
8
+ module Models
9
+ class StabilityAIAdapter < BaseAdapter
10
+ include ModelInterface
11
+
12
+ def configuration
13
+ @configuration ||= StabilityAI::Model::Configuration.new(engine_id: @model.name)
14
+ end
15
+
16
+ def call(operation, options, &block)
17
+ @model.call(operation, options, &block)
18
+ end
19
+
20
+ def text_to_image(options = {})
21
+ @model.call(:text_to_image, options)
22
+ end
23
+
24
+ def image_to_image(options = {})
25
+ @model.call(:image_to_image, options)
26
+ end
27
+
28
+ def upscale(options = {})
29
+ @model.call(:upscale, options)
30
+ end
31
+
32
+ def masking(options = {})
33
+ @model.call(:masking, options)
34
+ end
35
+
36
+ def operations
37
+ @model.operations
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "faraday/multipart"
5
+ require "faraday/retry"
6
+ require "faraday/typhoeus"
7
+ require "roseflow/types"
8
+ require "roseflow/stabilityai/config"
9
+ require "roseflow/stabilityai/model"
10
+
11
+ module Roseflow
12
+ module StabilityAI
13
+ class Client
14
+ FARADAY_RETRY_OPTIONS = {
15
+ max: 3,
16
+ interval: 0.05,
17
+ interval_randomness: 0.5,
18
+ backoff_factor: 2,
19
+ }
20
+
21
+ def initialize(config = Config.new, provider = nil)
22
+ @config = config
23
+ @provider = provider
24
+ end
25
+
26
+ def models
27
+ response = connection.get("/v1/engines/list")
28
+ body = JSON.parse(response.body)
29
+ end
30
+
31
+ def post(operation)
32
+ if operation.multipart?
33
+ response = multipart_connection.post(operation.path) do |request|
34
+ request.body = operation.body
35
+ end
36
+ else
37
+ response = connection.post(operation.path) do |request|
38
+ request.body = operation.body
39
+ end
40
+ end
41
+ response
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :config, :provider
47
+
48
+ def connection
49
+ @connection ||= Faraday.new(
50
+ url: config.api_url,
51
+ headers: {
52
+ "Stability-Client-ID" => config.client_id,
53
+ "Stability-Client-Version" => config.client_version,
54
+ },
55
+ ) do |faraday|
56
+ faraday.request :authorization, "Bearer", -> { config.api_key }
57
+ faraday.request :json
58
+ faraday.request :retry, FARADAY_RETRY_OPTIONS
59
+ faraday.adapter :typhoeus
60
+ end
61
+ end
62
+
63
+ def multipart_connection
64
+ @multipart_connection ||= Faraday.new(
65
+ url: config.api_url,
66
+ headers: {
67
+ "Stability-Client-ID" => config.client_id,
68
+ "Stability-Client-Version" => config.client_version,
69
+ },
70
+ ) do |faraday|
71
+ faraday.request :authorization, "Bearer", -> { config.api_key }
72
+ faraday.request :multipart
73
+ faraday.adapter :typhoeus
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyway_config"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ class Config < Anyway::Config
8
+ config_name :stabilityai
9
+
10
+ attr_config :api_key
11
+ attr_config api_url: "https://api.stability.ai"
12
+ attr_config client_id: "Roseflow Stability-AI Ruby Client"
13
+ attr_config client_version: "0.1.0"
14
+
15
+ required :api_key
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/stabilityai/operation_handler"
4
+ require "roseflow/stabilityai/response_handler"
5
+
6
+ module Roseflow
7
+ module StabilityAI
8
+ class Model
9
+ attr_reader :name
10
+
11
+ def initialize(model_config, provider = nil)
12
+ @model_config = model_config
13
+ @provider = provider
14
+ assign_attributes
15
+ end
16
+
17
+ def call(operation, options)
18
+ operation = OperationHandler.new(operation, options).call
19
+ ResponseHandler.new(operation, client.post(operation)).call
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :model_config
25
+ attr_reader :provider
26
+
27
+ def client
28
+ @client ||= Client.new(provider.send(:config), provider)
29
+ end
30
+
31
+ def assign_attributes
32
+ @name = model_config.fetch("id")
33
+ @description = model_config.fetch("description")
34
+ @descriptive_name = model_config.fetch("name")
35
+ @type = model_config.fetch("type")
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/delegation"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ class ModelRepository
8
+ attr_reader :models
9
+
10
+ delegate :each, :all, :first, :last, to: :models
11
+
12
+ def initialize(provider)
13
+ @provider = provider
14
+ @models = load_models
15
+ end
16
+
17
+ def find(name)
18
+ @models.select { |model| model.name == name }.first
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :provider
24
+
25
+ def load_models
26
+ provider.client.models.map do |model|
27
+ Model.new(model, provider)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "operations/text_to_image"
4
+ require_relative "operations/image_to_image"
5
+ require_relative "operations/upscale"
6
+ require_relative "operations/masking"
7
+
8
+ module Roseflow
9
+ module StabilityAI
10
+ class OperationHandler
11
+ OPERATION_CLASSES = {
12
+ text_to_image: Operations::TextToImage,
13
+ image_to_image: Operations::ImageToImage,
14
+ upscale: Operations::Upscale,
15
+ masking: Operations::Masking,
16
+ }
17
+
18
+ def initialize(operation, options = {})
19
+ @operation = operation
20
+ @options = options
21
+ end
22
+
23
+ def call
24
+ operation_class.new(@options)
25
+ end
26
+
27
+ private
28
+
29
+ def operation_class
30
+ OPERATION_CLASSES.fetch(@operation) do
31
+ raise ArgumentError, "Invalid operation: #{@operation}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Roseflow
4
+ module StabilityAI
5
+ module Operations
6
+ class Base < Dry::Struct
7
+ transform_keys(&:to_sym)
8
+
9
+ attribute :engine_id, Types::String
10
+
11
+ def excluded_keys
12
+ [:path]
13
+ end
14
+
15
+ def multipart?
16
+ true
17
+ end
18
+
19
+ def body
20
+ to_h.except(*excluded_keys)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/stabilityai/operations/base"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Operations
8
+ class ImageToImage < Base
9
+ attribute :text_prompts, Types::Array do
10
+ attribute :text, Types::String
11
+ attribute :weight, Types::Float.default(1.0)
12
+ end
13
+ attribute :init_image, Types::String
14
+ attribute :init_image_mode, Types::String.default("IMAGE_STRENGTH")
15
+ attribute :image_strength, Types::Float.default(0.35)
16
+ attribute :cfg_scale, Types::Integer.default(7)
17
+ attribute :clip_guidance_preset, Types::String.default("NONE")
18
+ attribute? :sampler, Types::String
19
+ attribute :samples, Types::Integer.default(1)
20
+ attribute :seed, Types::Integer.default(0)
21
+ attribute :style_preset, Types::String.default("photographic")
22
+
23
+ def path
24
+ "/v1/generation/#{engine_id}/image-to-image"
25
+ end
26
+
27
+ def type
28
+ :image_to_image
29
+ end
30
+
31
+ def excluded_keys
32
+ [:path, :engine_id, :text_prompts]
33
+ end
34
+
35
+ def body
36
+ initial = to_h.except(*excluded_keys)
37
+ initial.merge(
38
+ init_image: Faraday::Multipart::ParamPart.new(
39
+ init_image,
40
+ "image/png",
41
+ ),
42
+ text_prompts: text_prompts.each_with_index.map do |item, index|
43
+ [index, item.to_h]
44
+ end.to_h,
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/stabilityai/operations/base"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Operations
8
+ class Masking < Base
9
+ attribute :text_prompts, Types::Array do
10
+ attribute :text, Types::String
11
+ attribute :weight, Types::Float.default(1.0)
12
+ end
13
+ attribute :init_image, Types::String
14
+ attribute :mask_image, Types::String
15
+ attribute :mask_source, Types::String.default("INIT_IMAGE_ALPHA")
16
+ attribute :cfg_scale, Types::Integer.default(7)
17
+ attribute :clip_guidance_preset, Types::String.default("NONE")
18
+ attribute? :sampler, Types::String
19
+ attribute :samples, Types::Integer.default(1)
20
+ attribute :seed, Types::Integer.default(0)
21
+ attribute :steps, Types::Integer.default(50)
22
+ attribute :style_preset, Types::String.default("photographic")
23
+
24
+ def path
25
+ "/v1/generation/#{engine_id}/image-to-image/masking"
26
+ end
27
+
28
+ def excluded_keys
29
+ [:path, :engine_id, :text_prompts, :mask_image]
30
+ end
31
+
32
+ def body
33
+ to_h.except(*excluded_keys).merge(
34
+ init_image: Faraday::Multipart::ParamPart.new(
35
+ init_image,
36
+ "image/png",
37
+ ),
38
+ # mask_image: Faraday::Multipart::ParamPart.new(
39
+ # mask_image,
40
+ # "image/png",
41
+ # ),
42
+ text_prompts: text_prompts.each_with_index.map do |item, index|
43
+ [index, item.to_h]
44
+ end.to_h,
45
+ )
46
+ end
47
+
48
+ def type
49
+ :masking
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Operations
8
+ class TextToImage < Base
9
+ attribute :height, Types::Integer.default(512)
10
+ attribute :width, Types::Integer.default(512)
11
+ attribute :text_prompts, Types::Array do
12
+ attribute :text, Types::String
13
+ attribute :weight, Types::Float.default(1.0)
14
+ end
15
+ attribute :cfg_scale, Types::Integer.default(7)
16
+ attribute :clip_guidance_preset, Types::String.default("NONE")
17
+ attribute? :sampler, Types::String
18
+ attribute :samples, Types::Integer.default(1)
19
+ attribute :seed, Types::Integer.default(0)
20
+ attribute :steps, Types::Integer.default(50)
21
+ attribute? :style_preset, Types::String.default("photographic")
22
+
23
+ def type
24
+ :text_to_image
25
+ end
26
+
27
+ def multipart?
28
+ false
29
+ end
30
+
31
+ def path
32
+ "/v1/generation/#{engine_id}/text-to-image"
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/stabilityai/operations/base"
4
+
5
+ # TODO: Very sharp operation class currently, needs to handle edge cases better.
6
+ module Roseflow
7
+ module StabilityAI
8
+ module Operations
9
+ class Upscale < Base
10
+ attribute :upscale_type, Types::String
11
+
12
+ attribute? :esrgan do
13
+ attribute :image, Types::String
14
+ attribute? :width, Types::Integer
15
+ attribute? :height, Types::Integer
16
+ end
17
+
18
+ attribute? :sdx4 do
19
+ attribute :image, Types::String
20
+ attribute? :width, Types::Integer
21
+ attribute? :height, Types::Integer
22
+ attribute :text_prompts, Types::Array do
23
+ attribute :text, Types::String
24
+ attribute :weight, Types::Float.default(1.0)
25
+ end
26
+ attribute :seed, Types::Integer.default(0)
27
+ attribute :steps, Types::Integer.default(50)
28
+ attribute :cfg_scale, Types::Integer.default(7)
29
+ end
30
+
31
+ def path
32
+ "/v1/generation/#{engine_id}/image-to-image/upscale"
33
+ end
34
+
35
+ def excluded_keys
36
+ [:path, :engine_id, :text_prompts, :upscale_type, :esrgan, :sdx4]
37
+ end
38
+
39
+ def body
40
+ case upscale_type.to_sym
41
+ when :sdx4
42
+ sdx4_body
43
+ when :esrgan
44
+ esrgan_body
45
+ end
46
+ end
47
+
48
+ def sdx4_body
49
+ initial = to_h.except(*excluded_keys)
50
+ sdx4.to_h.merge(
51
+ image: Faraday::Multipart::ParamPart.new(
52
+ sdx4.image,
53
+ "image/png",
54
+ ),
55
+ text_prompts: sdx4.text_prompts.each_with_index.map do |item, index|
56
+ [index, item.to_h]
57
+ end.to_h,
58
+ ).merge(initial)
59
+ end
60
+
61
+ def esrgan_body
62
+ initial = to_h.except(*excluded_keys)
63
+ esrgan.to_h.merge(
64
+ image: Faraday::Multipart::ParamPart.new(
65
+ esrgan.image,
66
+ "image/png",
67
+ ),
68
+ ).merge(initial)
69
+ end
70
+
71
+ def type
72
+ :upscale
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Roseflow
4
+ module StabilityAI
5
+ class Provider
6
+ def initialize(config = Config.new)
7
+ @config = config
8
+ end
9
+
10
+ def models
11
+ @models ||= ModelRepository.new(self)
12
+ end
13
+
14
+ def client
15
+ @client ||= Client.new(config, self)
16
+ end
17
+
18
+ def call(operation, options, &block)
19
+ models.find(options.fetch(:engine_id)).call(operation, options, &block)
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :config
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/stabilityai/responses/text_to_image_response"
4
+ require "roseflow/stabilityai/responses/image_to_image_response"
5
+ require "roseflow/stabilityai/responses/upscale_response"
6
+ require "roseflow/stabilityai/responses/masking_response"
7
+ require "roseflow/stabilityai/responses/error_response"
8
+
9
+ module Roseflow
10
+ module StabilityAI
11
+ class ResponseHandler
12
+ def initialize(operation, response)
13
+ @operation = operation
14
+ @response = response
15
+ end
16
+
17
+ def call
18
+ if response.success?
19
+ handle_successful_response
20
+ else
21
+ handle_error_response
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :operation, :response
28
+
29
+ def handle_successful_response
30
+ json = JSON.parse(response.body)
31
+
32
+ case operation.type
33
+ when :text_to_image
34
+ Responses::TextToImageResponse.new(json)
35
+ when :image_to_image
36
+ Responses::ImageToImageResponse.new(json)
37
+ when :upscale
38
+ Responses::UpscaleResponse.new(json)
39
+ when :masking
40
+ Responses::MaskingResponse.new(json)
41
+ else
42
+ raise ArgumentError, "Invalid operation: #{operation.type}"
43
+ end
44
+ end
45
+
46
+ def handle_error_response
47
+ puts response.status
48
+ puts JSON.parse(response.body)
49
+ Responses::ErrorResponse.from(
50
+ code: response.status,
51
+ operation: operation.type,
52
+ body: JSON.parse(response.body),
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/struct"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Responses
8
+ class BaseResponse < Dry::Struct
9
+ transform_keys(&:to_sym)
10
+
11
+ def success?
12
+ true
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/struct"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Responses
8
+ class ErrorResponse < Dry::Struct
9
+ transform_keys(&:to_sym)
10
+
11
+ attribute :code, Types::Integer
12
+ attribute :operation, Types::String
13
+
14
+ def success?
15
+ false
16
+ end
17
+
18
+ # TODO: Implement this method
19
+ def self.from(code: nil, operation: nil, body: nil)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base_response"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Responses
8
+ class ImageToImageResponse < BaseResponse
9
+ transform_keys { |key| key.to_s.underscore.to_sym }
10
+
11
+ attribute :artifacts, Types::Array do
12
+ attribute :base64, Types::String
13
+ attribute :finish_reason, Types::String
14
+ attribute :seed, Types::Integer
15
+ end
16
+
17
+ alias_method :images, :artifacts
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base_response"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Responses
8
+ class MaskingResponse < BaseResponse
9
+ transform_keys { |key| key.to_s.underscore.to_sym }
10
+
11
+ attribute :artifacts, Types::Array do
12
+ attribute :base64, Types::String
13
+ attribute :finish_reason, Types::String
14
+ attribute :seed, Types::Integer
15
+ end
16
+
17
+ alias_method :images, :artifacts
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base_response"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Responses
8
+ class TextToImageResponse < BaseResponse
9
+ transform_keys { |key| key.to_s.underscore.to_sym }
10
+
11
+ attribute :artifacts, Types::Array do
12
+ attribute :base64, Types::String
13
+ attribute :finish_reason, Types::String
14
+ attribute :seed, Types::Integer
15
+ end
16
+
17
+ alias_method :images, :artifacts
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./base_response"
4
+
5
+ module Roseflow
6
+ module StabilityAI
7
+ module Responses
8
+ class UpscaleResponse < BaseResponse
9
+ transform_keys { |key| key.to_s.underscore.to_sym }
10
+
11
+ attribute :artifacts, Types::Array do
12
+ attribute :base64, Types::String
13
+ attribute :finish_reason, Types::String
14
+ attribute :seed, Types::Integer
15
+ end
16
+
17
+ alias_method :images, :artifacts
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Roseflow
4
+ module StabilityAI
5
+ def self.gem_version
6
+ Gem::Version.new VERSION::STRING
7
+ end
8
+
9
+ module VERSION
10
+ MAJOR = 0
11
+ MINOR = 1
12
+ PATCH = 0
13
+ PRE = nil
14
+
15
+ STRING = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roseflow/stabilityai/version"
4
+ require "roseflow/stabilityai/config"
5
+ require "roseflow/stabilityai/client"
6
+ require "roseflow/stabilityai/model_repository"
7
+ require "roseflow/stabilityai/model"
8
+ require "roseflow/stabilityai/operation_handler"
9
+ require "roseflow/stabilityai/provider"
10
+ require "roseflow/stabilityai/response_handler"
11
+
12
+ module Roseflow
13
+ module StabilityAI
14
+ class Error < StandardError; end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/roseflow/stabilityai/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "roseflow-stabilityai"
7
+ spec.version = Roseflow::StabilityAI.gem_version
8
+ spec.authors = ["Lauri Jutila"]
9
+ spec.email = ["ljuti@users.noreply.github.com"]
10
+
11
+ spec.summary = "Roseflow meets StabilityAI"
12
+ spec.description = "StabilityAI integration and models for Roseflow"
13
+ spec.homepage = "https://github.com/roseflow-ai/roseflow-stabilityai"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/roseflow-ai/roseflow-stabilityai"
19
+ spec.metadata["changelog_uri"] = "https://github.com/roseflow-ai/roseflow-stabilityai/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "activesupport"
33
+ spec.add_dependency "anyway_config", "~> 2.0"
34
+ spec.add_dependency "dry-struct"
35
+ spec.add_dependency "faraday"
36
+ spec.add_dependency "faraday-retry"
37
+ spec.add_dependency "faraday-typhoeus"
38
+ spec.add_dependency "faraday-multipart"
39
+
40
+ spec.add_development_dependency "awesome_print"
41
+ spec.add_development_dependency "pry"
42
+ spec.add_development_dependency "pry-byebug"
43
+ spec.add_development_dependency "roseflow"
44
+ spec.add_development_dependency "webmock"
45
+ spec.add_development_dependency "vcr"
46
+ end
@@ -0,0 +1,6 @@
1
+ module Roseflow
2
+ module Stabilityai
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,259 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roseflow-stabilityai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Lauri Jutila
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-08-09 00:00:00.000000000 Z
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: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: anyway_config
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-struct
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday-retry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faraday-typhoeus
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday-multipart
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: awesome_print
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: pry-byebug
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: roseflow
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: webmock
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: vcr
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: StabilityAI integration and models for Roseflow
196
+ email:
197
+ - ljuti@users.noreply.github.com
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - ".rspec"
203
+ - ".standard.yml"
204
+ - CHANGELOG.md
205
+ - CODE_OF_CONDUCT.md
206
+ - Gemfile
207
+ - LICENSE.txt
208
+ - README.md
209
+ - Rakefile
210
+ - lib/roseflow/ai/models/stabilityai_adapter.rb
211
+ - lib/roseflow/stabilityai.rb
212
+ - lib/roseflow/stabilityai/client.rb
213
+ - lib/roseflow/stabilityai/config.rb
214
+ - lib/roseflow/stabilityai/model.rb
215
+ - lib/roseflow/stabilityai/model_repository.rb
216
+ - lib/roseflow/stabilityai/operation_handler.rb
217
+ - lib/roseflow/stabilityai/operations/base.rb
218
+ - lib/roseflow/stabilityai/operations/image_to_image.rb
219
+ - lib/roseflow/stabilityai/operations/masking.rb
220
+ - lib/roseflow/stabilityai/operations/text_to_image.rb
221
+ - lib/roseflow/stabilityai/operations/upscale.rb
222
+ - lib/roseflow/stabilityai/provider.rb
223
+ - lib/roseflow/stabilityai/response_handler.rb
224
+ - lib/roseflow/stabilityai/responses/base_response.rb
225
+ - lib/roseflow/stabilityai/responses/error_response.rb
226
+ - lib/roseflow/stabilityai/responses/image_to_image_response.rb
227
+ - lib/roseflow/stabilityai/responses/masking_response.rb
228
+ - lib/roseflow/stabilityai/responses/text_to_image_response.rb
229
+ - lib/roseflow/stabilityai/responses/upscale_response.rb
230
+ - lib/roseflow/stabilityai/version.rb
231
+ - roseflow-stabilityai.gemspec
232
+ - sig/roseflow/stabilityai.rbs
233
+ homepage: https://github.com/roseflow-ai/roseflow-stabilityai
234
+ licenses:
235
+ - MIT
236
+ metadata:
237
+ homepage_uri: https://github.com/roseflow-ai/roseflow-stabilityai
238
+ source_code_uri: https://github.com/roseflow-ai/roseflow-stabilityai
239
+ changelog_uri: https://github.com/roseflow-ai/roseflow-stabilityai/blob/main/CHANGELOG.md
240
+ post_install_message:
241
+ rdoc_options: []
242
+ require_paths:
243
+ - lib
244
+ required_ruby_version: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: 3.2.0
249
+ required_rubygems_version: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - ">="
252
+ - !ruby/object:Gem::Version
253
+ version: '0'
254
+ requirements: []
255
+ rubygems_version: 3.4.1
256
+ signing_key:
257
+ specification_version: 4
258
+ summary: Roseflow meets StabilityAI
259
+ test_files: []