stability_ai 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/Gemfile +6 -0
- data/Gemfile.lock +79 -0
- data/README.md +73 -18
- data/lib/stability_ai/api/engines.rb +16 -0
- data/lib/stability_ai/api/generation.rb +176 -0
- data/lib/stability_ai/api/user.rb +16 -0
- data/lib/stability_ai/api.rb +4 -0
- data/lib/stability_ai/client.rb +39 -0
- data/lib/stability_ai/configuration.rb +12 -0
- data/lib/stability_ai/helpers/image_helper.rb +16 -0
- data/lib/stability_ai/response/base_response.rb +14 -0
- data/lib/stability_ai/response/engines_response.rb +9 -0
- data/lib/stability_ai/response/image_response.rb +39 -0
- data/lib/stability_ai/response/user_response.rb +10 -0
- data/lib/stability_ai/stability_ai_error.rb +14 -0
- data/lib/stability_ai/version.rb +2 -2
- data/lib/stability_ai.rb +25 -2
- data/stability_ai.gemspec +1 -2
- metadata +14 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a74f16129b63c646329f0a2f3b35048a4da347a1e6f7c5e4850e1ce4d6739cc9
|
4
|
+
data.tar.gz: 74b7aae52e67b6aa8c9225c68d42f68e84903438e8037d61ad09370a78a89021
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33772dce8b02317032bc6292d632101f03744e50e64bd3a59f21cb4e02540643455e2c06309fc5d30be60257b4cc53bdda31b6c87742f8e1b4c99d09b0c88f2e
|
7
|
+
data.tar.gz: d098914d24d40e6e35ad6f58b41a3240ed08553248c172a80bd665bdb73f9f37177cf9b4c75053cfc63324ae9076375a7a0deb795e5214b0312201416325eed5
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
stability_ai (0.1.2)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
byebug (11.1.3)
|
11
|
+
coderay (1.1.3)
|
12
|
+
diff-lcs (1.5.0)
|
13
|
+
httparty (0.20.0)
|
14
|
+
mime-types (~> 3.0)
|
15
|
+
multi_xml (>= 0.5.2)
|
16
|
+
json (2.6.3)
|
17
|
+
method_source (1.0.0)
|
18
|
+
mime-types (3.4.1)
|
19
|
+
mime-types-data (~> 3.2015)
|
20
|
+
mime-types-data (3.2023.0218.1)
|
21
|
+
multi_xml (0.6.0)
|
22
|
+
parallel (1.23.0)
|
23
|
+
parser (3.2.2.0)
|
24
|
+
ast (~> 2.4.1)
|
25
|
+
pkg-config (1.5.1)
|
26
|
+
pry (0.14.2)
|
27
|
+
coderay (~> 1.1)
|
28
|
+
method_source (~> 1.0)
|
29
|
+
pry-byebug (3.10.1)
|
30
|
+
byebug (~> 11.0)
|
31
|
+
pry (>= 0.13, < 0.15)
|
32
|
+
rainbow (3.1.1)
|
33
|
+
rake (13.0.6)
|
34
|
+
regexp_parser (2.8.0)
|
35
|
+
rexml (3.2.5)
|
36
|
+
rmagick (5.2.0)
|
37
|
+
pkg-config (~> 1.4)
|
38
|
+
rspec (3.12.0)
|
39
|
+
rspec-core (~> 3.12.0)
|
40
|
+
rspec-expectations (~> 3.12.0)
|
41
|
+
rspec-mocks (~> 3.12.0)
|
42
|
+
rspec-core (3.12.2)
|
43
|
+
rspec-support (~> 3.12.0)
|
44
|
+
rspec-expectations (3.12.3)
|
45
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
46
|
+
rspec-support (~> 3.12.0)
|
47
|
+
rspec-mocks (3.12.5)
|
48
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
49
|
+
rspec-support (~> 3.12.0)
|
50
|
+
rspec-support (3.12.0)
|
51
|
+
rubocop (1.50.2)
|
52
|
+
json (~> 2.3)
|
53
|
+
parallel (~> 1.10)
|
54
|
+
parser (>= 3.2.0.0)
|
55
|
+
rainbow (>= 2.2.2, < 4.0)
|
56
|
+
regexp_parser (>= 1.8, < 3.0)
|
57
|
+
rexml (>= 3.2.5, < 4.0)
|
58
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
59
|
+
ruby-progressbar (~> 1.7)
|
60
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
61
|
+
rubocop-ast (1.28.0)
|
62
|
+
parser (>= 3.2.1.0)
|
63
|
+
ruby-progressbar (1.13.0)
|
64
|
+
unicode-display_width (2.4.2)
|
65
|
+
|
66
|
+
PLATFORMS
|
67
|
+
x86_64-darwin-22
|
68
|
+
|
69
|
+
DEPENDENCIES
|
70
|
+
httparty (~> 0.20.0)
|
71
|
+
pry-byebug
|
72
|
+
rake (~> 13.0)
|
73
|
+
rmagick (~> 5.2.0)
|
74
|
+
rspec (~> 3.0)
|
75
|
+
rubocop (~> 1.21)
|
76
|
+
stability_ai!
|
77
|
+
|
78
|
+
BUNDLED WITH
|
79
|
+
2.4.12
|
data/README.md
CHANGED
@@ -1,39 +1,94 @@
|
|
1
|
-
#
|
1
|
+
# StabilityAI
|
2
2
|
|
3
|
-
|
3
|
+
StabilityAI is a Ruby gem that simplifies interactions with the Stability AI API. It supports image generation, image-to-image manipulation, upscaling, and masking.
|
4
4
|
|
5
|
-
|
5
|
+
## Demo
|
6
|
+
|
7
|
+
I made this `gem` part of a personal project: [ciel.chat](https://ciel.chat) - an AI supercharged WhatsApp bot, using ChatGPT, Bard, StableDiffusion, Dalle, GoogleSpeech, ElevenLabs.
|
8
|
+
You can try it there for free ✌️
|
9
|
+
|
10
|
+
## Disclaimer
|
11
|
+
|
12
|
+
It's my first gem ever. There are plenty of room for improvements there - feel free to contribute!
|
6
13
|
|
7
14
|
## Installation
|
8
15
|
|
9
|
-
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'stability_ai'
|
20
|
+
```
|
10
21
|
|
11
|
-
|
22
|
+
And then execute:
|
12
23
|
|
13
|
-
|
24
|
+
```ruby
|
25
|
+
$ bundle install
|
26
|
+
```
|
14
27
|
|
15
|
-
|
28
|
+
Or install it yourself as:
|
16
29
|
|
17
|
-
|
30
|
+
```ruby
|
31
|
+
gem install stability_ai
|
32
|
+
```
|
33
|
+
|
34
|
+
## Configuration
|
35
|
+
|
36
|
+
Create an initializer in your Rails project or any Ruby application (e.g., `config/initializers/stability_ai.rb`) to configure your API keys and credentials:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
StabilityAI.configure do |config|
|
40
|
+
config.stability_api_key = 'your_stability_api_key'
|
41
|
+
config.path_prefix = './'
|
42
|
+
end
|
43
|
+
```
|
18
44
|
|
19
45
|
## Usage
|
20
46
|
|
21
|
-
|
47
|
+
Here's an example of how to use the gem with the Stability AI API:
|
48
|
+
Check [StabilityAI API](https://api.stability.ai/docs) for all the options available. If there are not passed in the `options` hash, the default value is used.
|
22
49
|
|
23
|
-
|
50
|
+
```ruby
|
51
|
+
require 'stability_ai'
|
24
52
|
|
25
|
-
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 that will allow you to experiment.
|
26
53
|
|
27
|
-
|
54
|
+
client = StabilityAI::Client.new
|
28
55
|
|
29
|
-
|
56
|
+
# Text to image
|
57
|
+
text_to_image_response = client.text_to_image('engine_id', text_prompts: [{ text: 'A lighthouse on a cliff' }])
|
58
|
+
uri_image_1 = text_to_image_response.image_uris.first # -> <img src="#{uri_image_1}"/> or image_tag(uri_image_1)
|
59
|
+
text_to_image_response.save_images('your_image_name_prefix') # -> returns ["your_image_name_prefix_1.png", "your_image_name_prefix_2.png", ...]
|
30
60
|
|
31
|
-
|
61
|
+
# Image to image
|
32
62
|
|
33
|
-
|
63
|
+
options = {
|
64
|
+
text_prompts: [
|
65
|
+
{
|
66
|
+
text: "Snow",
|
67
|
+
}
|
68
|
+
],
|
69
|
+
image_strength: 0.35, # Default: 0.35 How much influence the init_image has on the diffusion process. Values close to 1 will yield images very similar to the init_image while values close to 0 will yield images wildly different than the init_image
|
70
|
+
cfg_scale: 7, # DEFAULT: [0..35] How strictly the diffusion process adheres to the prompt text (higher values keep your image closer to your prompt),
|
71
|
+
# clip_guidance_preset: "FAST_BLUE", # DEFAULT: NONE, WTF IS THAT?? FAST_BLUE FAST_GREEN NONE SIMPLE SLOW SLOWER SLOWEST
|
72
|
+
# sampler: "DDIM", # DEFAULT: Automatically choose by StableAI. DDIM DDPM K_DPMPP_2M K_DPMPP_2S_ANCESTRAL K_DPM_2 K_DPM_2_ANCESTRAL K_EULER K_EULER_ANCESTRAL K_HEUN K_LMS
|
73
|
+
sytle_preset: "neon-punk", # check
|
74
|
+
}
|
75
|
+
response = client.image_to_image(image_path: image_path, )
|
76
|
+
|
77
|
+
# Image to image upscale
|
78
|
+
response = client.image_to_image_upscale(engine_id: "esrgan-v1-x2plus", image_path: image_path, options: { width: 1024 })
|
79
|
+
response = client.image_to_image_upscale(image_path: image_path, use_maximum_resolution: true) # default engine: esrgan-v1-x2plus / use `use_maximum_resolution: true` is to use 2048px
|
80
|
+
response = client.image_to_image_upscale(image_binary: image_binary, )
|
34
81
|
|
35
|
-
|
82
|
+
# Image to image masking
|
83
|
+
# TODO
|
84
|
+
```
|
36
85
|
|
37
|
-
|
86
|
+
Replace `'engine_id'` with the appropriate engine ID for each method call.
|
87
|
+
|
88
|
+
## Contributing
|
89
|
+
|
90
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mael-ha/stability_ai.
|
91
|
+
|
92
|
+
## License
|
38
93
|
|
39
|
-
|
94
|
+
The gem is available as open source under the terms of the MIT License.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
module API
|
3
|
+
module Engines
|
4
|
+
def engines_list
|
5
|
+
response = self.class.get('/v1/engines/list')
|
6
|
+
if response.code == 200
|
7
|
+
StabilityAI::Response::EnginesListResponse.new(response)
|
8
|
+
else
|
9
|
+
error_data = response.parsed_response
|
10
|
+
raise StabilityAI::StabilityAIError.new(response.code, error_data["id"], error_data["name"], error_data["message"])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require "uri"
|
2
|
+
require "rmagick"
|
3
|
+
|
4
|
+
module StabilityAI
|
5
|
+
module API
|
6
|
+
module Generation
|
7
|
+
def text_to_image(engine_id: nil, options: {})
|
8
|
+
response = self.class.post("/v1/generation/#{get_engine_id(engine_id)}/text-to-image", body: options.to_json,
|
9
|
+
headers: { "Content-Type" => "application/json" })
|
10
|
+
handle_response(response)
|
11
|
+
end
|
12
|
+
|
13
|
+
def image_to_image(engine_id: nil, image_binary: nil, image_base64: nil, image_path: nil, options: {})
|
14
|
+
raise "No image file path provided." unless image_path || image_binary || image_base64
|
15
|
+
raise "No prompt provided." if options[:text_prompts].nil?
|
16
|
+
|
17
|
+
image_payload = create_payload(image_binary, image_base64, image_path)
|
18
|
+
image = convert_and_resize_image(image_payload)
|
19
|
+
temp_file = create_temp_file_from_image(image)
|
20
|
+
form_data = set_form_data(image:, endpoint: :image_to_image, options:, temp_file:)
|
21
|
+
|
22
|
+
default_weight = options[:text_prompts].count > 1 ? (1 / options[:text_prompts].count).round(2) : 1
|
23
|
+
options[:text_prompts].each_with_index do |text_prompt, i|
|
24
|
+
form_data.merge!("text_prompts[#{i}][text]" => text_prompt[:text]) unless text_prompt[:text].nil?
|
25
|
+
form_data.merge!("text_prompts[#{i}][weight]" => text_prompt[:weight] || default_weight) unless text_prompt[:weight].nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
headers = { "Content-Type" => "multipart/form-data" }
|
29
|
+
response = self.class.post("/v1/generation/#{get_engine_id(engine_id)}/image-to-image",
|
30
|
+
headers: headers, multipart: true, body: form_data)
|
31
|
+
handle_response(response)
|
32
|
+
ensure
|
33
|
+
if temp_file
|
34
|
+
temp_file.close
|
35
|
+
temp_file.unlink
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def image_to_image_upscale(engine_id: "esrgan-v1-x2plus", image_binary: nil, image_base64: nil, image_path: nil, use_maximum_resolution: false, options: {})
|
40
|
+
raise "No image file path provided." unless image_path || image_binary || image_base64
|
41
|
+
|
42
|
+
image_payload = create_payload(image_binary, image_base64, image_path)
|
43
|
+
image = convert_and_resize_image(image_payload)
|
44
|
+
temp_file = create_temp_file_from_image(image)
|
45
|
+
form_data = set_form_data(image:, endpoint: :image_to_image_upscale, options: options, temp_file:)
|
46
|
+
|
47
|
+
headers = { "Content-Type" => "multipart/form-data" }
|
48
|
+
response = self.class.post("/v1/generation/#{engine_id}/image-to-image/upscale", headers: headers, multipart: true, body: form_data)
|
49
|
+
handle_response(response)
|
50
|
+
ensure
|
51
|
+
if temp_file
|
52
|
+
temp_file.close
|
53
|
+
temp_file.unlink
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# def text_to_animation(engine_id: nil, options: {})
|
58
|
+
# response = self.class.post("/v1/generation/#{get_engine_id(engine_id)}/text-to-animation", body: options.to_json,
|
59
|
+
# headers: { "Content-Type" => "application/json" })
|
60
|
+
# handle_response(response)
|
61
|
+
# end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def get_engine_id(engine_id)
|
66
|
+
engine_id || StabilityAI.configuration.default_engine_id
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_response(response)
|
70
|
+
if response.code == 200
|
71
|
+
StabilityAI::Response::ImageResponse.new(response)
|
72
|
+
else
|
73
|
+
error_data = response.parsed_response
|
74
|
+
raise StabilityAI::StabilityAIError.new(response.code, error_data["id"], error_data["name"],
|
75
|
+
error_data["message"])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_payload(image_binary, image_base64, image_path)
|
80
|
+
if image_binary
|
81
|
+
StringIO.new(image_binary).read
|
82
|
+
elsif image_base64
|
83
|
+
StringIO.new(Base64.decode64(image_base64)).read
|
84
|
+
elsif image_path
|
85
|
+
File.read(File.open(image_path, 'rb'))
|
86
|
+
else
|
87
|
+
raise "No image binary, base64, or file path provided."
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_temp_file_from_image(image)
|
92
|
+
temp_file = Tempfile.new(['image', '.png'])
|
93
|
+
temp_file.binmode
|
94
|
+
image.format = 'PNG' # Set the format explicitly before calling to_blob
|
95
|
+
temp_file.write(image.to_blob)
|
96
|
+
temp_file.flush
|
97
|
+
temp_file.rewind
|
98
|
+
temp_file
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_image_dimensions(image)
|
102
|
+
[image.columns, image.rows]
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_max_dimension(image, options, use_maximum_resolution)
|
106
|
+
if use_maximum_resolution
|
107
|
+
width, height = get_image_dimensions(image)
|
108
|
+
(width > height) ? { 'width' => 2048 } : { 'height' => 2048 }
|
109
|
+
else
|
110
|
+
case
|
111
|
+
when !options[:width].nil?
|
112
|
+
{ 'width' => options[:width] }
|
113
|
+
when !options[:height].nil?
|
114
|
+
{ 'height' => options[:height] }
|
115
|
+
else
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def set_form_data(image:, endpoint:, options:, temp_file:, use_maximum_resolution: true)
|
122
|
+
raise "No image file provided." unless temp_file
|
123
|
+
|
124
|
+
form_data = {}
|
125
|
+
case endpoint
|
126
|
+
when :image_to_image
|
127
|
+
form_data.merge!("init_image" => temp_file)
|
128
|
+
parameters = %w[image_strength, init_image_mode, cfg_scale, clip_guidance_preset, samples, steps]
|
129
|
+
parameters.each do |parameter|
|
130
|
+
next if options[parameter.to_sym].nil?
|
131
|
+
form_data.merge!(parameter => options[parameter.to_sym])
|
132
|
+
end
|
133
|
+
form_data
|
134
|
+
when :image_to_image_upscale
|
135
|
+
form_data.merge!("image" => temp_file)
|
136
|
+
max_dimension = set_max_dimension(image, options, use_maximum_resolution)
|
137
|
+
form_data.merge!(max_dimension) if max_dimension
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_new_dimensions(width, height, step = 64)
|
142
|
+
new_width = (width / step) * step
|
143
|
+
new_height = (height / step) * step
|
144
|
+
[new_width, new_height]
|
145
|
+
end
|
146
|
+
|
147
|
+
def convert_and_resize_image(image_payload)
|
148
|
+
# Convert the input image to PNG format if it's a JPG or JPEG file
|
149
|
+
image = Magick::Image.from_blob(image_payload).first
|
150
|
+
image.format = 'PNG' if %w[JPG JPEG].include?(image.format)
|
151
|
+
|
152
|
+
if image.columns % 64 != 0 || image.rows % 64 != 0
|
153
|
+
# Calculate new dimensions
|
154
|
+
new_width, new_height = get_new_dimensions(image.columns, image.rows)
|
155
|
+
|
156
|
+
# Resize the image while maintaining aspect ratio
|
157
|
+
image.change_geometry("#{new_width}x#{new_height}") do |cols, rows, img|
|
158
|
+
img.resize!(cols, rows)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Find the nearest multiples of 64 for both width and height
|
162
|
+
width_multiple = (image.columns / 64.0).floor * 64
|
163
|
+
height_multiple = (image.rows / 64.0).floor * 64
|
164
|
+
|
165
|
+
# Calculate new offsets for cropping
|
166
|
+
x_offset = (image.columns - width_multiple) / 2
|
167
|
+
y_offset = (image.rows - height_multiple) / 2
|
168
|
+
|
169
|
+
# Crop the centered part to ensure dimensions are multiples of 64
|
170
|
+
image.crop!(x_offset, y_offset, width_multiple, height_multiple)
|
171
|
+
end
|
172
|
+
image
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
module API
|
3
|
+
module User
|
4
|
+
def balance
|
5
|
+
response = self.class.get('/v1/user/balance')
|
6
|
+
if response.code == 200
|
7
|
+
StabilityAI::Response::UserResponse.new(response)
|
8
|
+
else
|
9
|
+
error_data = response.parsed_response
|
10
|
+
raise StabilityAI::StabilityAIError.new(response.code, error_data["id"], error_data["name"], error_data["message"])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
class Client
|
3
|
+
include HTTParty
|
4
|
+
include StabilityAI::API::User
|
5
|
+
include StabilityAI::API::Engines
|
6
|
+
include StabilityAI::API::Generation
|
7
|
+
|
8
|
+
base_uri "https://api.stability.ai"
|
9
|
+
format :json
|
10
|
+
|
11
|
+
def initialize(api_key = nil)
|
12
|
+
@api_key = api_key || StabilityAI.configuration.stability_api_key || ENV["STABILITY_API_KEY"]
|
13
|
+
raise ArgumentError, "Missing Stability API key." unless @api_key
|
14
|
+
|
15
|
+
self.class.default_options.merge!(
|
16
|
+
headers: {
|
17
|
+
"Authorization" => "Bearer #{@api_key}",
|
18
|
+
"Content-Type" => "application/json",
|
19
|
+
"Accept" => "application/json"
|
20
|
+
}
|
21
|
+
)
|
22
|
+
|
23
|
+
set_default_engine_id
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def set_default_engine_id
|
29
|
+
# Setting stable-diffusion-xl as default engine
|
30
|
+
return if StabilityAI.configuration.default_engine_id
|
31
|
+
|
32
|
+
engines_list_response = engines_list
|
33
|
+
engines = engines_list_response.engines
|
34
|
+
|
35
|
+
default_engine = engines.find { |engine| engine.id.include?("stable-diffusion-xl") }
|
36
|
+
StabilityAI.configuration.default_engine_id = default_engine.id if default_engine
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
module Helpers
|
3
|
+
class ImageHelper
|
4
|
+
def self.save_artifacts(artifacts, prefix = "output")
|
5
|
+
artifacts.each_with_index do |artifact, i|
|
6
|
+
save_base64_image("#{prefix}_#{i}.png", artifact.base64)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.save_base64_image(file_path, base64_image)
|
11
|
+
File.binwrite(file_path, Base64.decode64(base64_image))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
module Response
|
3
|
+
class BaseResponse
|
4
|
+
attr_reader :raw_response, :http_status
|
5
|
+
|
6
|
+
def initialize(raw_response)
|
7
|
+
@raw_response = raw_response
|
8
|
+
@parsed_response = @raw_response.parsed_response
|
9
|
+
@http_status = raw_response.code
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
module Response
|
3
|
+
class ImageResponse < BaseResponse
|
4
|
+
attr_reader :artifacts
|
5
|
+
|
6
|
+
def initialize(response)
|
7
|
+
super(response)
|
8
|
+
@artifacts = response.parsed_response["artifacts"]
|
9
|
+
convert_artifacts_to_openstruct
|
10
|
+
end
|
11
|
+
|
12
|
+
def convert_artifacts_to_openstruct
|
13
|
+
@artifacts.map! do |artifact|
|
14
|
+
image_binary_data = Base64.decode64(artifact["base64"])
|
15
|
+
artifact["image_binary"] = image_binary_data
|
16
|
+
artifact.delete("base64")
|
17
|
+
OpenStruct.new(artifact)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def save_images(filename_prefix: nil)
|
22
|
+
filename_prefix = Time.now.utc.strftime("%Y%m%d%H%M%S") if filename_prefix.nil?
|
23
|
+
downloaded_images = []
|
24
|
+
path_prefix = StabilityAI.configuration.path_prefix
|
25
|
+
@artifacts.each_with_index do |artifact, i|
|
26
|
+
image_name = "#{filename_prefix}_#{i}.png"
|
27
|
+
image_path = path_prefix + image_name
|
28
|
+
File.binwrite(image_path, artifact["image_binary"])
|
29
|
+
downloaded_images << {
|
30
|
+
file_name: image_name,
|
31
|
+
seed: artifact["seed"],
|
32
|
+
finish_reason: artifact["finishReason"],
|
33
|
+
}
|
34
|
+
end
|
35
|
+
downloaded_images
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module StabilityAI
|
2
|
+
class StabilityAIError < StandardError
|
3
|
+
attr_reader :id, :name, :message
|
4
|
+
|
5
|
+
def initialize(code, id, name, message)
|
6
|
+
@code = code
|
7
|
+
@id = id
|
8
|
+
@name = name
|
9
|
+
@message= message
|
10
|
+
super("#{code}# #{name}: #{message} (ID: #{id})")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
data/lib/stability_ai/version.rb
CHANGED
data/lib/stability_ai.rb
CHANGED
@@ -1,8 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "httparty"
|
4
|
+
require "dotenv/load"
|
5
|
+
require "base64"
|
6
|
+
require "uri"
|
3
7
|
require_relative "stability_ai/version"
|
8
|
+
require_relative "stability_ai/api"
|
9
|
+
require_relative "stability_ai/api/user"
|
10
|
+
require_relative "stability_ai/api/engines"
|
11
|
+
require_relative "stability_ai/api/generation"
|
12
|
+
require_relative "stability_ai/client"
|
13
|
+
require_relative "stability_ai/response/base_response"
|
14
|
+
require_relative "stability_ai/response/user_response"
|
15
|
+
require_relative "stability_ai/response/engines_response"
|
16
|
+
require_relative "stability_ai/response/image_response"
|
17
|
+
require_relative "stability_ai/stability_ai_error"
|
18
|
+
require_relative "stability_ai/configuration"
|
19
|
+
|
20
|
+
module StabilityAI
|
21
|
+
class << self
|
22
|
+
attr_accessor :configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configure
|
26
|
+
self.configuration ||= Configuration.new
|
27
|
+
yield(configuration)
|
28
|
+
end
|
4
29
|
|
5
|
-
module StabilityAi
|
6
30
|
class Error < StandardError; end
|
7
|
-
# Your code goes here...
|
8
31
|
end
|
data/stability_ai.gemspec
CHANGED
@@ -4,7 +4,7 @@ require_relative "lib/stability_ai/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "stability_ai"
|
7
|
-
spec.version =
|
7
|
+
spec.version = StabilityAI::VERSION
|
8
8
|
spec.authors = ["Maël Harnois"]
|
9
9
|
spec.email = ["m@maelus.com"]
|
10
10
|
|
@@ -13,7 +13,6 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.license = "MIT"
|
14
14
|
spec.required_ruby_version = ">= 2.6.0"
|
15
15
|
|
16
|
-
|
17
16
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
17
|
spec.metadata["source_code_uri"] = "https://github.com/mael-ha/stability_ai"
|
19
18
|
spec.metadata["changelog_uri"] = "https://github.com/mael-ha/stability_ai/blob/main/CHANGELOG.md"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stability_ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maël Harnois
|
@@ -22,10 +22,23 @@ files:
|
|
22
22
|
- CHANGELOG.md
|
23
23
|
- CODE_OF_CONDUCT.md
|
24
24
|
- Gemfile
|
25
|
+
- Gemfile.lock
|
25
26
|
- LICENSE.txt
|
26
27
|
- README.md
|
27
28
|
- Rakefile
|
28
29
|
- lib/stability_ai.rb
|
30
|
+
- lib/stability_ai/api.rb
|
31
|
+
- lib/stability_ai/api/engines.rb
|
32
|
+
- lib/stability_ai/api/generation.rb
|
33
|
+
- lib/stability_ai/api/user.rb
|
34
|
+
- lib/stability_ai/client.rb
|
35
|
+
- lib/stability_ai/configuration.rb
|
36
|
+
- lib/stability_ai/helpers/image_helper.rb
|
37
|
+
- lib/stability_ai/response/base_response.rb
|
38
|
+
- lib/stability_ai/response/engines_response.rb
|
39
|
+
- lib/stability_ai/response/image_response.rb
|
40
|
+
- lib/stability_ai/response/user_response.rb
|
41
|
+
- lib/stability_ai/stability_ai_error.rb
|
29
42
|
- lib/stability_ai/version.rb
|
30
43
|
- sig/stability_ai.rbs
|
31
44
|
- stability_ai.gemspec
|