reve_ai 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +272 -2
- data/lib/reve_ai/client.rb +28 -1
- data/lib/reve_ai/configuration.rb +10 -1
- data/lib/reve_ai/errors.rb +15 -4
- data/lib/reve_ai/http/client.rb +100 -18
- data/lib/reve_ai/resources/base.rb +63 -7
- data/lib/reve_ai/resources/effects.rb +83 -0
- data/lib/reve_ai/resources/images.rb +100 -16
- data/lib/reve_ai/resources/v2/images.rb +188 -0
- data/lib/reve_ai/resources/v2/layouts.rb +309 -0
- data/lib/reve_ai/resources/v2.rb +42 -0
- data/lib/reve_ai/response.rb +108 -17
- data/lib/reve_ai/version.rb +1 -1
- data/lib/reve_ai.rb +4 -0
- metadata +22 -2
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../base"
|
|
4
|
+
|
|
5
|
+
module ReveAI
|
|
6
|
+
module Resources
|
|
7
|
+
class V2
|
|
8
|
+
# v2 layout pipeline operations (experimental).
|
|
9
|
+
#
|
|
10
|
+
# The layout endpoints expose lower-level control over image
|
|
11
|
+
# composition: extracting structured layouts from images, generating
|
|
12
|
+
# layouts from prompts, and rendering images from layouts.
|
|
13
|
+
#
|
|
14
|
+
# @note Experimental: these endpoints require care and experimentation
|
|
15
|
+
# to achieve good results. For simple image generation and
|
|
16
|
+
# prompt-based editing, prefer {V2::Images#create}.
|
|
17
|
+
#
|
|
18
|
+
# @see https://api.reve.com/console/docs Reve API Documentation
|
|
19
|
+
class Layouts < Base
|
|
20
|
+
# @return [String] API endpoint for layout extraction
|
|
21
|
+
EXTRACT_ENDPOINT = "/v2/image/extract_layout"
|
|
22
|
+
|
|
23
|
+
# @return [String] API endpoint for layout generation
|
|
24
|
+
CREATE_ENDPOINT = "/v2/image/create_layout"
|
|
25
|
+
|
|
26
|
+
# @return [String] API endpoint for layout rendering
|
|
27
|
+
RENDER_ENDPOINT = "/v2/image/render_layout"
|
|
28
|
+
|
|
29
|
+
# Extracts a structured layout from an image.
|
|
30
|
+
#
|
|
31
|
+
# @param image [Hash] Source image: exactly one of +data+ (base64
|
|
32
|
+
# encoded image String) or +ref+ (identifier String: +"id:<uuid>"+
|
|
33
|
+
# or +"reference:@<name>"+)
|
|
34
|
+
# @param prompt [String, nil] Optional instruction for transforming
|
|
35
|
+
# the extracted layout (max 4000 chars)
|
|
36
|
+
# @param version [String, nil] Optional public model version alias
|
|
37
|
+
# @param breadcrumb [String, nil] Request tracking value sent as the
|
|
38
|
+
# +breadcrumb+ query parameter; ignored by the API
|
|
39
|
+
#
|
|
40
|
+
# @return [LayoutResponse] Response containing the extracted +layout+
|
|
41
|
+
#
|
|
42
|
+
# @raise [ValidationError] if image is malformed or prompt exceeds max length
|
|
43
|
+
#
|
|
44
|
+
# @example Extract a layout from an image
|
|
45
|
+
# result = client.v2.layouts.extract(image: { data: photo_base64 })
|
|
46
|
+
# result.layout # => { prompt: "...", regions: [...], width: 4672, height: 3520 }
|
|
47
|
+
#
|
|
48
|
+
# @note Experimental: layout extraction commonly takes 10-40 seconds.
|
|
49
|
+
def extract(image:, prompt: nil, version: nil, breadcrumb: nil)
|
|
50
|
+
validate_raw_image!(image, "Image")
|
|
51
|
+
validate_prompt!(prompt, max_length: Configuration::V2_MAX_PROMPT_LENGTH) if prompt
|
|
52
|
+
|
|
53
|
+
body = { image: image }
|
|
54
|
+
body[:prompt] = prompt if prompt
|
|
55
|
+
body[:version] = version if version
|
|
56
|
+
|
|
57
|
+
response = post(EXTRACT_ENDPOINT, body, params: breadcrumb_params(breadcrumb))
|
|
58
|
+
LayoutResponse.new(status: response.status, headers: response.headers, body: response.body)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Generates (or edits) a structured layout without rendering an image.
|
|
62
|
+
#
|
|
63
|
+
# @param prompt [String, nil] Description of the desired layout
|
|
64
|
+
# (max 4000 chars); at least one of +prompt+ or +references+ is required
|
|
65
|
+
# @param references [Array<Hash>, nil] Up to 8 ordered compound
|
|
66
|
+
# references ({Configuration::V2_MAX_REFERENCES}); each entry may
|
|
67
|
+
# contain +image+ (a raw { data: }/{ ref: } image Hash), +layout+
|
|
68
|
+
# (a layout Hash), and/or +prompt+ (String) — at least one per entry
|
|
69
|
+
# @param commands [Array<Hash>, nil] Ordered imperative layout edits;
|
|
70
|
+
# each entry is a Hash with an +op+ key (add, place, shift, remove,
|
|
71
|
+
# keep, change) plus op-specific fields
|
|
72
|
+
# @param aspect_ratio [String, nil] Layout aspect ratio; supported set
|
|
73
|
+
# ({Configuration::ASPECT_RATIOS}), default "auto"
|
|
74
|
+
# @param version [String, nil] Optional public model version alias
|
|
75
|
+
# @param breadcrumb [String, nil] Request tracking value sent as the
|
|
76
|
+
# +breadcrumb+ query parameter; ignored by the API
|
|
77
|
+
#
|
|
78
|
+
# @return [LayoutResponse] Response containing the generated +layout+
|
|
79
|
+
#
|
|
80
|
+
# @raise [ValidationError] if neither prompt nor references is given,
|
|
81
|
+
# or any argument is malformed
|
|
82
|
+
#
|
|
83
|
+
# @example Free-form layout from a prompt
|
|
84
|
+
# result = client.v2.layouts.create(prompt: "a person at a cafe")
|
|
85
|
+
# result.layout[:regions] # => [{ label: "person", bbox: {...}, ... }]
|
|
86
|
+
#
|
|
87
|
+
# @note Experimental: layout generation commonly takes 10-40 seconds.
|
|
88
|
+
def create(prompt: nil, references: nil, commands: nil, aspect_ratio: nil, version: nil, breadcrumb: nil)
|
|
89
|
+
validate_prompt_or_references!(prompt, references)
|
|
90
|
+
validate_prompt!(prompt, max_length: Configuration::V2_MAX_PROMPT_LENGTH) if prompt
|
|
91
|
+
validate_compound_references!(references)
|
|
92
|
+
validate_commands!(commands)
|
|
93
|
+
validate_aspect_ratio!(aspect_ratio, Configuration::ASPECT_RATIOS)
|
|
94
|
+
|
|
95
|
+
body = build_create_body(prompt: prompt, references: references, commands: commands,
|
|
96
|
+
aspect_ratio: aspect_ratio, version: version)
|
|
97
|
+
|
|
98
|
+
response = post(CREATE_ENDPOINT, body, params: breadcrumb_params(breadcrumb))
|
|
99
|
+
LayoutResponse.new(status: response.status, headers: response.headers, body: response.body)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Renders a final image from a target layout.
|
|
103
|
+
#
|
|
104
|
+
# @param layout [Hash] The layout to render; must include a non-empty
|
|
105
|
+
# +regions+ Array (each region: +label+, +prompt+, +bbox+ with
|
|
106
|
+
# normalized x0/y0/x1/y1; optional +parent+, +region_type+,
|
|
107
|
+
# +image_index+, +image_region_index+)
|
|
108
|
+
# @param references [Array<Hash>, nil] Up to 8 ordered compound
|
|
109
|
+
# references; same shape as {#create}
|
|
110
|
+
# @param postprocessing [Array<Hash>, nil] Postprocessing steps, each
|
|
111
|
+
# with a +process+ key (e.g., { process: "fit_image", max_dim: 2048 })
|
|
112
|
+
# @param version [String, nil] Optional public model version alias
|
|
113
|
+
# @param accept [String, nil] Per-request Accept header: "image/png",
|
|
114
|
+
# "image/jpeg", or "image/webp" for a binary image response, or
|
|
115
|
+
# "application/json" (default)
|
|
116
|
+
# @param breadcrumb [String, nil] Request tracking value sent as the
|
|
117
|
+
# +breadcrumb+ query parameter; ignored by the API
|
|
118
|
+
#
|
|
119
|
+
# @return [ImageResponse] Response containing the rendered image and
|
|
120
|
+
# the produced +layout+
|
|
121
|
+
#
|
|
122
|
+
# @raise [ValidationError] if layout is missing or malformed
|
|
123
|
+
#
|
|
124
|
+
# @example Render a layout to an image
|
|
125
|
+
# layout = { regions: [{ label: "cat", prompt: "a tabby cat",
|
|
126
|
+
# bbox: { x0: 0.2, y0: 0.2, x1: 0.8, y1: 0.8 } }] }
|
|
127
|
+
# result = client.v2.layouts.render(layout: layout)
|
|
128
|
+
# File.binwrite("cat.png", Base64.decode64(result.image))
|
|
129
|
+
#
|
|
130
|
+
# @note Experimental: rendering commonly takes 40-80 seconds.
|
|
131
|
+
def render(layout:, references: nil, postprocessing: nil, version: nil, accept: nil, breadcrumb: nil)
|
|
132
|
+
validate_layout!(layout)
|
|
133
|
+
validate_compound_references!(references)
|
|
134
|
+
validate_postprocessing!(postprocessing)
|
|
135
|
+
|
|
136
|
+
body = { layout: layout }
|
|
137
|
+
body[:references] = references if references
|
|
138
|
+
body[:postprocessing] = postprocessing if postprocessing
|
|
139
|
+
body[:version] = version if version
|
|
140
|
+
|
|
141
|
+
response = post(RENDER_ENDPOINT, body, params: breadcrumb_params(breadcrumb), accept: accept)
|
|
142
|
+
ImageResponse.new(status: response.status, headers: response.headers, body: response.body)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
# Builds the request body for the create_layout endpoint.
|
|
148
|
+
#
|
|
149
|
+
# @return [Hash] Request body with only the provided options
|
|
150
|
+
# @api private
|
|
151
|
+
def build_create_body(prompt:, references:, commands:, aspect_ratio:, version:)
|
|
152
|
+
body = {}
|
|
153
|
+
body[:prompt] = prompt if prompt
|
|
154
|
+
body[:references] = references if references
|
|
155
|
+
body[:commands] = commands if commands
|
|
156
|
+
body[:aspect_ratio] = aspect_ratio if aspect_ratio
|
|
157
|
+
body[:version] = version if version
|
|
158
|
+
body
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Returns the query params Hash for a breadcrumb, or nil.
|
|
162
|
+
#
|
|
163
|
+
# @param breadcrumb [String, nil] Breadcrumb value
|
|
164
|
+
# @return [Hash, nil] Query params
|
|
165
|
+
# @api private
|
|
166
|
+
def breadcrumb_params(breadcrumb)
|
|
167
|
+
breadcrumb ? { breadcrumb: breadcrumb } : nil
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Reads a Hash value accepting symbol or string keys.
|
|
171
|
+
#
|
|
172
|
+
# @param hash [Hash] The Hash to read
|
|
173
|
+
# @param key [Symbol] The key to look up (symbol or string form)
|
|
174
|
+
# @return [Object, nil] The value, or nil when absent
|
|
175
|
+
# @api private
|
|
176
|
+
def fetch_value(hash, key)
|
|
177
|
+
hash[key] || hash[key.to_s]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Validates that at least one of prompt or references is present.
|
|
181
|
+
#
|
|
182
|
+
# @raise [ValidationError] if both are nil/empty
|
|
183
|
+
# @api private
|
|
184
|
+
def validate_prompt_or_references!(prompt, references)
|
|
185
|
+
return if prompt && !prompt.empty?
|
|
186
|
+
return if references && !references.empty?
|
|
187
|
+
|
|
188
|
+
raise ValidationError, "At least one of prompt or references is required"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Validates a raw image object ({ data: } or { ref: } shape).
|
|
192
|
+
#
|
|
193
|
+
# @param image [Object] The value to validate
|
|
194
|
+
# @param label [String] Label for error messages
|
|
195
|
+
# @raise [ValidationError] if not a Hash with exactly one non-empty
|
|
196
|
+
# String +data+ or +ref+ value
|
|
197
|
+
# @api private
|
|
198
|
+
def validate_raw_image!(image, label)
|
|
199
|
+
raise ValidationError, "#{label} must be a Hash with exactly one of 'data' or 'ref'" unless image.is_a?(Hash)
|
|
200
|
+
|
|
201
|
+
data = fetch_value(image, :data)
|
|
202
|
+
ref = fetch_value(image, :ref)
|
|
203
|
+
|
|
204
|
+
raise ValidationError, "#{label} must include exactly one of 'data' or 'ref'" if data.nil? == ref.nil?
|
|
205
|
+
|
|
206
|
+
value = data || ref
|
|
207
|
+
return if value.is_a?(String) && !value.empty?
|
|
208
|
+
|
|
209
|
+
raise ValidationError, "#{label} '#{data ? "data" : "ref"}' must be a non-empty String"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Validates compound layout references.
|
|
213
|
+
#
|
|
214
|
+
# @param references [Array<Hash>, nil] Compound reference entries
|
|
215
|
+
# @raise [ValidationError] if not an Array of valid compound entries
|
|
216
|
+
# @api private
|
|
217
|
+
def validate_compound_references!(references)
|
|
218
|
+
return if references.nil?
|
|
219
|
+
|
|
220
|
+
unless references.is_a?(Array)
|
|
221
|
+
raise ValidationError, "References must be an Array of compound reference Hashes"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
max = Configuration::V2_MAX_REFERENCES
|
|
225
|
+
raise ValidationError, "Maximum #{max} references allowed" if references.length > max
|
|
226
|
+
|
|
227
|
+
references.each_with_index { |reference, index| validate_compound_reference!(reference, index) }
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Validates a single compound reference entry.
|
|
231
|
+
#
|
|
232
|
+
# @param reference [Hash] Entry with at least one of image/layout/prompt
|
|
233
|
+
# @param index [Integer] Position in the references array
|
|
234
|
+
# @raise [ValidationError] if the entry carries none of the allowed
|
|
235
|
+
# fields, or carries a malformed image/layout
|
|
236
|
+
# @api private
|
|
237
|
+
def validate_compound_reference!(reference, index)
|
|
238
|
+
raise ValidationError, "Reference at index #{index} must be a Hash" unless reference.is_a?(Hash)
|
|
239
|
+
|
|
240
|
+
image = fetch_value(reference, :image)
|
|
241
|
+
layout = fetch_value(reference, :layout)
|
|
242
|
+
prompt = fetch_value(reference, :prompt)
|
|
243
|
+
|
|
244
|
+
if image.nil? && layout.nil? && prompt.nil?
|
|
245
|
+
raise ValidationError,
|
|
246
|
+
"Reference at index #{index} must include at least one of 'image', 'layout', 'prompt'"
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
validate_raw_image!(image, "Reference at index #{index} 'image'") if image
|
|
250
|
+
validate_reference_layout!(layout, index)
|
|
251
|
+
validate_reference_prompt!(prompt, index)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Validates the layout value inside a compound reference.
|
|
255
|
+
#
|
|
256
|
+
# @param layout [Object] The value to check
|
|
257
|
+
# @param index [Integer] Position in the references array
|
|
258
|
+
# @raise [ValidationError] if present and not a Hash
|
|
259
|
+
# @api private
|
|
260
|
+
def validate_reference_layout!(layout, index)
|
|
261
|
+
return if layout.nil? || layout.is_a?(Hash)
|
|
262
|
+
|
|
263
|
+
raise ValidationError, "Reference at index #{index} 'layout' must be a Hash"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Validates the prompt value inside a compound reference.
|
|
267
|
+
#
|
|
268
|
+
# @param prompt [Object] The value to check
|
|
269
|
+
# @param index [Integer] Position in the references array
|
|
270
|
+
# @raise [ValidationError] if present and not a non-empty String
|
|
271
|
+
# @api private
|
|
272
|
+
def validate_reference_prompt!(prompt, index)
|
|
273
|
+
return if prompt.nil?
|
|
274
|
+
return if prompt.is_a?(String) && !prompt.empty?
|
|
275
|
+
|
|
276
|
+
raise ValidationError, "Reference at index #{index} 'prompt' must be a non-empty String"
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Validates the target layout for rendering.
|
|
280
|
+
#
|
|
281
|
+
# @param layout [Object] The layout to validate
|
|
282
|
+
# @raise [ValidationError] if not a Hash with a non-empty regions Array
|
|
283
|
+
# @api private
|
|
284
|
+
def validate_layout!(layout)
|
|
285
|
+
regions = fetch_value(layout, :regions) if layout.is_a?(Hash)
|
|
286
|
+
return if regions.is_a?(Array) && !regions.empty?
|
|
287
|
+
|
|
288
|
+
raise ValidationError, "Layout must be a Hash with a non-empty 'regions' Array"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Validates layout commands.
|
|
292
|
+
#
|
|
293
|
+
# @param commands [Array<Hash>, nil] Command entries
|
|
294
|
+
# @raise [ValidationError] if not an Array of Hashes with an +op+ key
|
|
295
|
+
# @api private
|
|
296
|
+
def validate_commands!(commands)
|
|
297
|
+
return if commands.nil?
|
|
298
|
+
raise ValidationError, "Commands must be an Array of Hashes with an 'op' key" unless commands.is_a?(Array)
|
|
299
|
+
|
|
300
|
+
commands.each_with_index do |command, index|
|
|
301
|
+
next if command.is_a?(Hash) && (command.key?(:op) || command.key?("op"))
|
|
302
|
+
|
|
303
|
+
raise ValidationError, "Command at index #{index} must be a Hash with an 'op' key"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ReveAI
|
|
4
|
+
module Resources
|
|
5
|
+
# Namespace for Reve API v2 resources.
|
|
6
|
+
#
|
|
7
|
+
# Accessed via `client.v2`. Groups the v2 image create endpoint and the
|
|
8
|
+
# experimental layout pipeline endpoints.
|
|
9
|
+
#
|
|
10
|
+
# @example Generate an image with the v2 API
|
|
11
|
+
# result = client.v2.images.create(prompt: "A sunset over mountains")
|
|
12
|
+
# result.layout # => { prompt: "...", regions: [...] }
|
|
13
|
+
#
|
|
14
|
+
# @see https://api.reve.com/console/docs Reve API Documentation
|
|
15
|
+
class V2
|
|
16
|
+
# @return [Client] The client instance for this namespace
|
|
17
|
+
attr_reader :client
|
|
18
|
+
|
|
19
|
+
# Creates a new v2 namespace.
|
|
20
|
+
#
|
|
21
|
+
# @param client [Client] The API client
|
|
22
|
+
# @api private
|
|
23
|
+
def initialize(client)
|
|
24
|
+
@client = client
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Returns the v2 images resource.
|
|
28
|
+
#
|
|
29
|
+
# @return [V2::Images] v2 image generation operations
|
|
30
|
+
def images
|
|
31
|
+
@images ||= Images.new(client)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns the v2 layouts resource (experimental endpoints).
|
|
35
|
+
#
|
|
36
|
+
# @return [V2::Layouts] v2 layout pipeline operations
|
|
37
|
+
def layouts
|
|
38
|
+
@layouts ||= Layouts.new(client)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/reve_ai/response.rb
CHANGED
|
@@ -3,9 +3,13 @@
|
|
|
3
3
|
module ReveAI
|
|
4
4
|
# Base response wrapper for API responses.
|
|
5
5
|
#
|
|
6
|
-
# Provides access to HTTP status, headers, and
|
|
6
|
+
# Provides access to HTTP status, headers, and response body. The body is a
|
|
7
|
+
# parsed Hash for JSON responses, or the raw image String (bytes) when the
|
|
8
|
+
# API answers with a binary body (Accept: image/*); in that case all
|
|
9
|
+
# metadata is carried by the X-Reve-* response headers.
|
|
7
10
|
#
|
|
8
11
|
# @see ImageResponse
|
|
12
|
+
# @see LayoutResponse
|
|
9
13
|
class Response
|
|
10
14
|
# @return [Integer] HTTP status code
|
|
11
15
|
attr_reader :status
|
|
@@ -13,14 +17,15 @@ module ReveAI
|
|
|
13
17
|
# @return [Hash] Response headers
|
|
14
18
|
attr_reader :headers
|
|
15
19
|
|
|
16
|
-
# @return [Hash] Parsed response body
|
|
20
|
+
# @return [Hash, String] Parsed response body, or raw bytes String
|
|
21
|
+
# for binary (image/*) responses
|
|
17
22
|
attr_reader :body
|
|
18
23
|
|
|
19
24
|
# Creates a new response wrapper.
|
|
20
25
|
#
|
|
21
26
|
# @param status [Integer] HTTP status code
|
|
22
27
|
# @param headers [Hash] Response headers
|
|
23
|
-
# @param body [Hash] Parsed response body
|
|
28
|
+
# @param body [Hash, String] Parsed response body, or raw bytes String
|
|
24
29
|
def initialize(status:, headers:, body:)
|
|
25
30
|
@status = status
|
|
26
31
|
@headers = headers
|
|
@@ -34,20 +39,42 @@ module ReveAI
|
|
|
34
39
|
status >= 200 && status < 300
|
|
35
40
|
end
|
|
36
41
|
|
|
42
|
+
# Checks if the response body is raw binary data (e.g., image bytes).
|
|
43
|
+
#
|
|
44
|
+
# @return [Boolean] true if body is not a parsed JSON Hash
|
|
45
|
+
def binary?
|
|
46
|
+
!body.is_a?(Hash)
|
|
47
|
+
end
|
|
48
|
+
|
|
37
49
|
# Returns the request ID for this response.
|
|
38
50
|
#
|
|
39
51
|
# Useful for debugging and support requests.
|
|
40
52
|
#
|
|
41
53
|
# @return [String, nil] Request ID from body or headers
|
|
42
54
|
def request_id
|
|
43
|
-
|
|
55
|
+
body_value(:request_id) || headers["x-reve-request-id"]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# Reads a key from the body when it is a Hash (JSON response).
|
|
61
|
+
#
|
|
62
|
+
# Binary (String) bodies have no keys; header fallbacks apply instead.
|
|
63
|
+
#
|
|
64
|
+
# @param key [Symbol] Body key to read
|
|
65
|
+
# @return [Object, nil] Body value, or nil for binary bodies
|
|
66
|
+
# @api private
|
|
67
|
+
def body_value(key)
|
|
68
|
+
body[key] if body.is_a?(Hash)
|
|
44
69
|
end
|
|
45
70
|
end
|
|
46
71
|
|
|
47
72
|
# Response wrapper for image generation API responses.
|
|
48
73
|
#
|
|
49
74
|
# Provides convenient accessors for image data, version info,
|
|
50
|
-
# content policy status, and credit usage.
|
|
75
|
+
# content policy status, and credit usage. All accessors work for both
|
|
76
|
+
# JSON responses (values from the parsed body) and binary responses
|
|
77
|
+
# (values from the X-Reve-* headers).
|
|
51
78
|
#
|
|
52
79
|
# @example Accessing image data
|
|
53
80
|
# result = client.images.create(prompt: "A sunset")
|
|
@@ -66,28 +93,48 @@ module ReveAI
|
|
|
66
93
|
#
|
|
67
94
|
# @see Response
|
|
68
95
|
class ImageResponse < Response
|
|
69
|
-
# Returns the
|
|
96
|
+
# Returns the image data.
|
|
70
97
|
#
|
|
71
|
-
#
|
|
98
|
+
# For JSON responses this is the base64 encoded image; for binary
|
|
99
|
+
# responses (Accept: image/*) it is the raw image bytes in the
|
|
100
|
+
# negotiated format, ready to write to disk without decoding.
|
|
72
101
|
#
|
|
73
|
-
# @return [String, nil] Base64 encoded
|
|
102
|
+
# @return [String, nil] Base64 encoded image data (JSON response) or
|
|
103
|
+
# raw image bytes (binary response)
|
|
74
104
|
#
|
|
75
|
-
# @example Save to file
|
|
105
|
+
# @example Save a JSON (base64) response to file
|
|
76
106
|
# require "base64"
|
|
77
|
-
#
|
|
78
|
-
#
|
|
107
|
+
# File.binwrite("output.png", Base64.decode64(result.image))
|
|
108
|
+
#
|
|
109
|
+
# @example Save a binary response to file (no Base64 decode needed)
|
|
110
|
+
# File.binwrite("output.webp", result.image)
|
|
79
111
|
def image
|
|
80
|
-
body[:image]
|
|
112
|
+
binary? ? body : body[:image]
|
|
81
113
|
end
|
|
82
114
|
|
|
83
115
|
# Alias for {#image}.
|
|
84
116
|
#
|
|
85
|
-
# @
|
|
117
|
+
# @note Despite the name, binary responses (Accept: image/*) return raw
|
|
118
|
+
# image bytes here, not base64 data.
|
|
119
|
+
#
|
|
120
|
+
# @return [String, nil] Base64 encoded image data (JSON response) or
|
|
121
|
+
# raw image bytes (binary response)
|
|
86
122
|
# @see #image
|
|
87
123
|
def base64
|
|
88
124
|
image
|
|
89
125
|
end
|
|
90
126
|
|
|
127
|
+
# Returns the layout object for this generation.
|
|
128
|
+
#
|
|
129
|
+
# Present on v2 create/render JSON responses; nil on v1 responses and
|
|
130
|
+
# on binary (Accept: image/*) responses.
|
|
131
|
+
#
|
|
132
|
+
# @return [Hash, nil] Layout Hash (e.g., +prompt+, +regions+, +width+,
|
|
133
|
+
# +height+), or nil when absent
|
|
134
|
+
def layout
|
|
135
|
+
body_value(:layout)
|
|
136
|
+
end
|
|
137
|
+
|
|
91
138
|
# Returns the model version used for generation.
|
|
92
139
|
#
|
|
93
140
|
# @return [String, nil] Model version (e.g., "reve-create@20250915")
|
|
@@ -95,7 +142,7 @@ module ReveAI
|
|
|
95
142
|
# @example
|
|
96
143
|
# result.version # => "reve-create@20250915"
|
|
97
144
|
def version
|
|
98
|
-
|
|
145
|
+
body_value(:version) || headers["x-reve-version"]
|
|
99
146
|
end
|
|
100
147
|
|
|
101
148
|
# Checks if the generated image violates content policy.
|
|
@@ -107,7 +154,7 @@ module ReveAI
|
|
|
107
154
|
# puts "Warning: Content policy violated"
|
|
108
155
|
# end
|
|
109
156
|
def content_violation?
|
|
110
|
-
|
|
157
|
+
body_value(:content_violation) == true ||
|
|
111
158
|
headers["x-reve-content-violation"] == "true"
|
|
112
159
|
end
|
|
113
160
|
|
|
@@ -118,7 +165,7 @@ module ReveAI
|
|
|
118
165
|
# @example
|
|
119
166
|
# puts "This request used #{result.credits_used} credits"
|
|
120
167
|
def credits_used
|
|
121
|
-
|
|
168
|
+
body_value(:credits_used) || headers["x-reve-credits-used"]&.to_i
|
|
122
169
|
end
|
|
123
170
|
|
|
124
171
|
# Returns the number of credits remaining after this request.
|
|
@@ -130,7 +177,51 @@ module ReveAI
|
|
|
130
177
|
# puts "Warning: Low credit balance"
|
|
131
178
|
# end
|
|
132
179
|
def credits_remaining
|
|
133
|
-
|
|
180
|
+
body_value(:credits_remaining) || headers["x-reve-credits-remaining"]&.to_i
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Response wrapper for layout-only API responses.
|
|
185
|
+
#
|
|
186
|
+
# Returned by the v2 extract_layout and create_layout endpoints, which
|
|
187
|
+
# produce a layout object but no image. Accessors fall back to the
|
|
188
|
+
# X-Reve-* headers when the body carries no value.
|
|
189
|
+
#
|
|
190
|
+
# @example Inspecting a layout
|
|
191
|
+
# result = client.v2.layouts.extract(image: base64_image)
|
|
192
|
+
# result.layout # => { prompt: "...", regions: [...], width: 4096, height: 2560 }
|
|
193
|
+
#
|
|
194
|
+
# @see Response
|
|
195
|
+
# @see ImageResponse
|
|
196
|
+
class LayoutResponse < Response
|
|
197
|
+
# Returns the layout object.
|
|
198
|
+
#
|
|
199
|
+
# @return [Hash, nil] Layout Hash (e.g., +prompt+, +regions+, +width+,
|
|
200
|
+
# +height+), or nil when absent
|
|
201
|
+
def layout
|
|
202
|
+
body_value(:layout)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Checks if the request violates content policy.
|
|
206
|
+
#
|
|
207
|
+
# @return [Boolean] true if content policy was violated
|
|
208
|
+
def content_violation?
|
|
209
|
+
body_value(:content_violation) == true ||
|
|
210
|
+
headers["x-reve-content-violation"] == "true"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Returns the number of credits used for this request.
|
|
214
|
+
#
|
|
215
|
+
# @return [Integer, nil] Credits consumed by this request
|
|
216
|
+
def credits_used
|
|
217
|
+
body_value(:credits_used) || headers["x-reve-credits-used"]&.to_i
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Returns the number of credits remaining after this request.
|
|
221
|
+
#
|
|
222
|
+
# @return [Integer, nil] Remaining credit balance
|
|
223
|
+
def credits_remaining
|
|
224
|
+
body_value(:credits_remaining) || headers["x-reve-credits-remaining"]&.to_i
|
|
134
225
|
end
|
|
135
226
|
end
|
|
136
227
|
end
|
data/lib/reve_ai/version.rb
CHANGED
data/lib/reve_ai.rb
CHANGED
|
@@ -7,6 +7,10 @@ require_relative "reve_ai/response"
|
|
|
7
7
|
require_relative "reve_ai/http/client"
|
|
8
8
|
require_relative "reve_ai/resources/base"
|
|
9
9
|
require_relative "reve_ai/resources/images"
|
|
10
|
+
require_relative "reve_ai/resources/effects"
|
|
11
|
+
require_relative "reve_ai/resources/v2"
|
|
12
|
+
require_relative "reve_ai/resources/v2/images"
|
|
13
|
+
require_relative "reve_ai/resources/v2/layouts"
|
|
10
14
|
require_relative "reve_ai/client"
|
|
11
15
|
|
|
12
16
|
# Ruby client for the Reve image generation API.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reve_ai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dpaluy
|
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.3'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.3'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: faraday
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -44,8 +58,10 @@ email:
|
|
|
44
58
|
executables: []
|
|
45
59
|
extensions: []
|
|
46
60
|
extra_rdoc_files:
|
|
61
|
+
- CHANGELOG.md
|
|
47
62
|
- README.md
|
|
48
63
|
files:
|
|
64
|
+
- CHANGELOG.md
|
|
49
65
|
- README.md
|
|
50
66
|
- Rakefile
|
|
51
67
|
- lib/reve_ai.rb
|
|
@@ -54,7 +70,11 @@ files:
|
|
|
54
70
|
- lib/reve_ai/errors.rb
|
|
55
71
|
- lib/reve_ai/http/client.rb
|
|
56
72
|
- lib/reve_ai/resources/base.rb
|
|
73
|
+
- lib/reve_ai/resources/effects.rb
|
|
57
74
|
- lib/reve_ai/resources/images.rb
|
|
75
|
+
- lib/reve_ai/resources/v2.rb
|
|
76
|
+
- lib/reve_ai/resources/v2/images.rb
|
|
77
|
+
- lib/reve_ai/resources/v2/layouts.rb
|
|
58
78
|
- lib/reve_ai/response.rb
|
|
59
79
|
- lib/reve_ai/version.rb
|
|
60
80
|
homepage: https://github.com/dpaluy/reve_ai
|
|
@@ -81,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
81
101
|
- !ruby/object:Gem::Version
|
|
82
102
|
version: '0'
|
|
83
103
|
requirements: []
|
|
84
|
-
rubygems_version:
|
|
104
|
+
rubygems_version: 3.6.9
|
|
85
105
|
specification_version: 4
|
|
86
106
|
summary: Ruby client for the Reve image generation API.
|
|
87
107
|
test_files: []
|