ai-lite 0.3.0 → 0.4.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/README.md +65 -0
- data/lib/ai_lite/version.rb +1 -1
- data/lib/ai_lite.rb +70 -3
- data/test/ai_lite_test.rb +139 -0
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f75f42caf4d74f3abf2ec982b7c3830c715b1d1c54f2ae0fdf02a1dec5151db
|
|
4
|
+
data.tar.gz: ff953a4533862067eee149c04ac5f348bf3b97e7e9f7b87fbcd18a3fe3d63233
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2c91c198575156c15e0100c3fa12709b1cf09395785d4a634e46bf06e9bc9dbf9e8073a95e990f54c4e19a0b72322379317299443899d83eef5c587864b6781
|
|
7
|
+
data.tar.gz: c528951faf3fe8f453def5a8dbaf7eb00c7da77fc5135c22ccfe2ac5765a2603a0c5cf50dcdfcbe1dda7aaedf6f69ca210a19424b67ef2866c627cd40e17104e
|
data/README.md
CHANGED
|
@@ -19,6 +19,7 @@ It is not meant to replace the official OpenAI SDK. It is a small wrapper for pr
|
|
|
19
19
|
ai.chat("Say hello")
|
|
20
20
|
ai.moderate("User submitted text")
|
|
21
21
|
ai.embed("Text to vectorize")
|
|
22
|
+
ai.image("A simple app icon")
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
## Usage
|
|
@@ -51,6 +52,7 @@ AiLite.configure do |config|
|
|
|
51
52
|
config.model = "gpt-5.5"
|
|
52
53
|
config.moderation_model = "omni-moderation-latest"
|
|
53
54
|
config.embedding_model = "text-embedding-3-small"
|
|
55
|
+
config.image_model = "gpt-image-2"
|
|
54
56
|
config.timeout = 120
|
|
55
57
|
config.max_output_tokens = 2000
|
|
56
58
|
end
|
|
@@ -239,6 +241,69 @@ result["content"] # embedding vector
|
|
|
239
241
|
result["raw"]["usage"] # token usage
|
|
240
242
|
```
|
|
241
243
|
|
|
244
|
+
## Images
|
|
245
|
+
|
|
246
|
+
Use `image` to generate an image from a prompt.
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
result = ai.image("A clean Ruby gem logo on a white background")
|
|
250
|
+
image_data = result["content"]
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
By default, `content` is the base64-encoded generated image:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
{
|
|
257
|
+
"content" => "iVBORw0KGgo...",
|
|
258
|
+
"response_id" => nil,
|
|
259
|
+
"status" => 200,
|
|
260
|
+
"error" => nil,
|
|
261
|
+
"raw" => nil
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Write the generated image bytes directly to a file with `output_path`:
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
result = ai.image(
|
|
269
|
+
"A clean Ruby gem logo on a white background",
|
|
270
|
+
output_path: "tmp/logo.png"
|
|
271
|
+
)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`image` sends a `POST` request to `/v1/images/generations` with:
|
|
275
|
+
|
|
276
|
+
- `model`
|
|
277
|
+
- `prompt`
|
|
278
|
+
- optional `size`
|
|
279
|
+
- optional `quality`
|
|
280
|
+
- optional `background`
|
|
281
|
+
- optional `output_format`
|
|
282
|
+
- optional `debug`
|
|
283
|
+
- optional extra `options`
|
|
284
|
+
|
|
285
|
+
The default image model is `gpt-image-2`.
|
|
286
|
+
|
|
287
|
+
Use `output_format` to request `png`, `webp`, or `jpeg` output:
|
|
288
|
+
|
|
289
|
+
```ruby
|
|
290
|
+
result = ai.image(
|
|
291
|
+
"A transparent app icon",
|
|
292
|
+
background: "transparent",
|
|
293
|
+
output_format: "webp",
|
|
294
|
+
output_path: "tmp/icon.webp"
|
|
295
|
+
)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Pass `debug: true` to include the raw OpenAI response, including usage when returned:
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
result = ai.image("A tiny robot sticker", debug: true)
|
|
302
|
+
|
|
303
|
+
result["content"] # base64 image data
|
|
304
|
+
result["raw"]["usage"] # token usage, when returned
|
|
305
|
+
```
|
|
306
|
+
|
|
242
307
|
## Multi-Turn Chat
|
|
243
308
|
|
|
244
309
|
Responses include a `response_id` that can be passed back through `options` as `previous_response_id`:
|
data/lib/ai_lite/version.rb
CHANGED
data/lib/ai_lite.rb
CHANGED
|
@@ -9,6 +9,7 @@ class AiLite
|
|
|
9
9
|
DEFAULT_MODEL = "gpt-5.5".freeze
|
|
10
10
|
DEFAULT_MODERATION_MODEL = "omni-moderation-latest".freeze
|
|
11
11
|
DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small".freeze
|
|
12
|
+
DEFAULT_IMAGE_MODEL = "gpt-image-2".freeze
|
|
12
13
|
DEFAULT_TIMEOUT = 120
|
|
13
14
|
DEFAULT_MAX_OUTPUT_TOKENS = 2000
|
|
14
15
|
IMAGE_MIME_TYPES = {
|
|
@@ -20,13 +21,14 @@ class AiLite
|
|
|
20
21
|
}.freeze
|
|
21
22
|
|
|
22
23
|
class Configuration
|
|
23
|
-
attr_accessor :api_key, :model, :moderation_model, :embedding_model, :timeout, :max_output_tokens
|
|
24
|
+
attr_accessor :api_key, :model, :moderation_model, :embedding_model, :image_model, :timeout, :max_output_tokens
|
|
24
25
|
|
|
25
26
|
def initialize
|
|
26
27
|
@api_key = nil
|
|
27
28
|
@model = DEFAULT_MODEL
|
|
28
29
|
@moderation_model = DEFAULT_MODERATION_MODEL
|
|
29
30
|
@embedding_model = DEFAULT_EMBEDDING_MODEL
|
|
31
|
+
@image_model = DEFAULT_IMAGE_MODEL
|
|
30
32
|
@timeout = DEFAULT_TIMEOUT
|
|
31
33
|
@max_output_tokens = DEFAULT_MAX_OUTPUT_TOKENS
|
|
32
34
|
end
|
|
@@ -65,20 +67,25 @@ class AiLite
|
|
|
65
67
|
client.embed(input, **kwargs)
|
|
66
68
|
end
|
|
67
69
|
|
|
70
|
+
def image(prompt, **kwargs)
|
|
71
|
+
client.image(prompt, **kwargs)
|
|
72
|
+
end
|
|
73
|
+
|
|
68
74
|
def reset_client!
|
|
69
75
|
@client = nil
|
|
70
76
|
end
|
|
71
77
|
end
|
|
72
78
|
|
|
73
|
-
attr_reader :api_key, :model, :moderation_model, :embedding_model, :timeout, :max_output_tokens, :headers
|
|
79
|
+
attr_reader :api_key, :model, :moderation_model, :embedding_model, :image_model, :timeout, :max_output_tokens, :headers
|
|
74
80
|
|
|
75
|
-
def initialize(api_key: nil, model: nil, moderation_model: nil, embedding_model: nil, timeout: nil, max_output_tokens: nil)
|
|
81
|
+
def initialize(api_key: nil, model: nil, moderation_model: nil, embedding_model: nil, image_model: nil, timeout: nil, max_output_tokens: nil)
|
|
76
82
|
@api_key = api_key || self.class.configuration.api_key || ENV["OPENAI_API_KEY"] || ENV["OPEN_AI_TOKEN"]
|
|
77
83
|
raise ArgumentError, "Missing OpenAI API key" if @api_key.to_s.strip.empty?
|
|
78
84
|
|
|
79
85
|
@model = model || self.class.configuration.model
|
|
80
86
|
@moderation_model = moderation_model || self.class.configuration.moderation_model
|
|
81
87
|
@embedding_model = embedding_model || self.class.configuration.embedding_model
|
|
88
|
+
@image_model = image_model || self.class.configuration.image_model
|
|
82
89
|
@timeout = timeout || self.class.configuration.timeout
|
|
83
90
|
@max_output_tokens = max_output_tokens || self.class.configuration.max_output_tokens
|
|
84
91
|
@headers = {
|
|
@@ -124,6 +131,21 @@ class AiLite
|
|
|
124
131
|
prettify_data(status: "unknown", error: e.message, raw: nil, debug: debug)
|
|
125
132
|
end
|
|
126
133
|
|
|
134
|
+
def image(prompt, model: nil, size: nil, quality: nil, background: nil, output_format: nil, output_path: nil, debug: false, options: {})
|
|
135
|
+
payload = options.merge(
|
|
136
|
+
model: model || image_model,
|
|
137
|
+
prompt: prompt
|
|
138
|
+
)
|
|
139
|
+
payload[:size] = size if size
|
|
140
|
+
payload[:quality] = quality if quality
|
|
141
|
+
payload[:background] = background if background
|
|
142
|
+
payload[:output_format] = output_format if output_format
|
|
143
|
+
|
|
144
|
+
extract_image(post(payload, endpoint: image_endpoint), output_path: output_path, debug: debug)
|
|
145
|
+
rescue => e
|
|
146
|
+
prettify_data(status: "unknown", error: e.message, raw: nil, debug: debug)
|
|
147
|
+
end
|
|
148
|
+
|
|
127
149
|
private
|
|
128
150
|
|
|
129
151
|
def post(payload, endpoint: response_endpoint)
|
|
@@ -155,6 +177,10 @@ class AiLite
|
|
|
155
177
|
"#{API_BASE_URL}/embeddings"
|
|
156
178
|
end
|
|
157
179
|
|
|
180
|
+
def image_endpoint
|
|
181
|
+
"#{API_BASE_URL}/images/generations"
|
|
182
|
+
end
|
|
183
|
+
|
|
158
184
|
def extract_content(response, debug: false)
|
|
159
185
|
status = response.code.to_i
|
|
160
186
|
parsed_response = JSON.parse(response.body)
|
|
@@ -238,6 +264,36 @@ class AiLite
|
|
|
238
264
|
prettify_data(status: response_status(response), error: e.message, raw: nil, debug: debug)
|
|
239
265
|
end
|
|
240
266
|
|
|
267
|
+
def extract_image(response, output_path:, debug: false)
|
|
268
|
+
status = response.code.to_i
|
|
269
|
+
parsed_response = JSON.parse(response.body)
|
|
270
|
+
|
|
271
|
+
unless success_status?(status)
|
|
272
|
+
return prettify_data(
|
|
273
|
+
status: status,
|
|
274
|
+
error: error_message(parsed_response),
|
|
275
|
+
response_id: parsed_response["id"],
|
|
276
|
+
raw: parsed_response,
|
|
277
|
+
debug: debug
|
|
278
|
+
)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
content = image_content(parsed_response)
|
|
282
|
+
write_image_output(output_path, content) if output_path
|
|
283
|
+
|
|
284
|
+
prettify_data(
|
|
285
|
+
status: status,
|
|
286
|
+
content: content,
|
|
287
|
+
response_id: parsed_response["id"],
|
|
288
|
+
raw: parsed_response,
|
|
289
|
+
debug: debug
|
|
290
|
+
)
|
|
291
|
+
rescue JSON::ParserError => e
|
|
292
|
+
prettify_data(status: response_status(response), error: e.message, raw: response&.body, debug: debug)
|
|
293
|
+
rescue => e
|
|
294
|
+
prettify_data(status: response_status(response), error: e.message, raw: nil, debug: debug)
|
|
295
|
+
end
|
|
296
|
+
|
|
241
297
|
def extract_output_text(raw)
|
|
242
298
|
Array(raw["output"]).flat_map do |item|
|
|
243
299
|
next [] unless item.is_a?(Hash) && item["type"] == "message"
|
|
@@ -312,6 +368,17 @@ class AiLite
|
|
|
312
368
|
multiple ? embeddings : embeddings.first
|
|
313
369
|
end
|
|
314
370
|
|
|
371
|
+
def image_content(raw)
|
|
372
|
+
image = Array(raw["data"]).find { |item| item.is_a?(Hash) && item["b64_json"] }
|
|
373
|
+
image && image["b64_json"]
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def write_image_output(path, content)
|
|
377
|
+
raise "No image data returned" if content.to_s.empty?
|
|
378
|
+
|
|
379
|
+
File.binwrite(path, Base64.decode64(content))
|
|
380
|
+
end
|
|
381
|
+
|
|
315
382
|
def success_status?(status)
|
|
316
383
|
status >= 200 && status < 300
|
|
317
384
|
end
|
data/test/ai_lite_test.rb
CHANGED
|
@@ -33,6 +33,7 @@ class AiLiteTest < Minitest::Test
|
|
|
33
33
|
client = AiLite.new(
|
|
34
34
|
api_key: "explicit-key",
|
|
35
35
|
model: "gpt-test",
|
|
36
|
+
image_model: "gpt-image-test",
|
|
36
37
|
timeout: 10
|
|
37
38
|
)
|
|
38
39
|
|
|
@@ -40,6 +41,7 @@ class AiLiteTest < Minitest::Test
|
|
|
40
41
|
assert_equal "gpt-test", client.model
|
|
41
42
|
assert_equal "omni-moderation-latest", client.moderation_model
|
|
42
43
|
assert_equal "text-embedding-3-small", client.embedding_model
|
|
44
|
+
assert_equal "gpt-image-test", client.image_model
|
|
43
45
|
assert_equal 10, client.timeout
|
|
44
46
|
assert_equal 2000, client.max_output_tokens
|
|
45
47
|
assert_equal "Bearer explicit-key", client.headers["Authorization"]
|
|
@@ -54,6 +56,7 @@ class AiLiteTest < Minitest::Test
|
|
|
54
56
|
config.model = "gpt-config"
|
|
55
57
|
config.moderation_model = "omni-moderation-test"
|
|
56
58
|
config.embedding_model = "text-embedding-test"
|
|
59
|
+
config.image_model = "gpt-image-test"
|
|
57
60
|
config.timeout = 15
|
|
58
61
|
config.max_output_tokens = 750
|
|
59
62
|
end
|
|
@@ -64,6 +67,7 @@ class AiLiteTest < Minitest::Test
|
|
|
64
67
|
assert_equal "gpt-config", client.model
|
|
65
68
|
assert_equal "omni-moderation-test", client.moderation_model
|
|
66
69
|
assert_equal "text-embedding-test", client.embedding_model
|
|
70
|
+
assert_equal "gpt-image-test", client.image_model
|
|
67
71
|
assert_equal 15, client.timeout
|
|
68
72
|
assert_equal 750, client.max_output_tokens
|
|
69
73
|
assert_same client, AiLite.client
|
|
@@ -87,6 +91,7 @@ class AiLiteTest < Minitest::Test
|
|
|
87
91
|
config.model = "gpt-config"
|
|
88
92
|
config.moderation_model = "omni-moderation-config"
|
|
89
93
|
config.embedding_model = "text-embedding-config"
|
|
94
|
+
config.image_model = "gpt-image-config"
|
|
90
95
|
config.timeout = 15
|
|
91
96
|
config.max_output_tokens = 750
|
|
92
97
|
end
|
|
@@ -96,6 +101,7 @@ class AiLiteTest < Minitest::Test
|
|
|
96
101
|
model: "gpt-explicit",
|
|
97
102
|
moderation_model: "omni-moderation-explicit",
|
|
98
103
|
embedding_model: "text-embedding-explicit",
|
|
104
|
+
image_model: "gpt-image-explicit",
|
|
99
105
|
timeout: 5,
|
|
100
106
|
max_output_tokens: 300
|
|
101
107
|
)
|
|
@@ -104,6 +110,7 @@ class AiLiteTest < Minitest::Test
|
|
|
104
110
|
assert_equal "gpt-explicit", client.model
|
|
105
111
|
assert_equal "omni-moderation-explicit", client.moderation_model
|
|
106
112
|
assert_equal "text-embedding-explicit", client.embedding_model
|
|
113
|
+
assert_equal "gpt-image-explicit", client.image_model
|
|
107
114
|
assert_equal 5, client.timeout
|
|
108
115
|
assert_equal 300, client.max_output_tokens
|
|
109
116
|
end
|
|
@@ -525,6 +532,118 @@ class AiLiteTest < Minitest::Test
|
|
|
525
532
|
end
|
|
526
533
|
end
|
|
527
534
|
|
|
535
|
+
def test_image_sends_post_to_images_with_default_payload
|
|
536
|
+
client = AiLite.new(api_key: "token-abc")
|
|
537
|
+
image_data = Base64.strict_encode64("fake image")
|
|
538
|
+
|
|
539
|
+
with_stubbed_http(image_response(b64_json: image_data)) do |captured, _response|
|
|
540
|
+
result = client.image("A small ruby gem logo")
|
|
541
|
+
request = captured[:http].last_request
|
|
542
|
+
payload = JSON.parse(request.body)
|
|
543
|
+
|
|
544
|
+
assert_equal image_data, result["content"]
|
|
545
|
+
assert_nil result["response_id"]
|
|
546
|
+
assert_equal 200, result["status"]
|
|
547
|
+
assert_nil result["error"]
|
|
548
|
+
assert_nil result["raw"]
|
|
549
|
+
assert_equal "api.openai.com", captured[:host]
|
|
550
|
+
assert_equal 443, captured[:port]
|
|
551
|
+
assert_equal true, captured[:use_ssl]
|
|
552
|
+
assert_instance_of Net::HTTP::Post, request
|
|
553
|
+
assert_equal "/v1/images/generations", request.path
|
|
554
|
+
assert_equal "Bearer token-abc", request["Authorization"]
|
|
555
|
+
assert_equal "application/json", request["Content-Type"]
|
|
556
|
+
assert_equal "gpt-image-2", payload["model"]
|
|
557
|
+
assert_equal "A small ruby gem logo", payload["prompt"]
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def test_image_includes_options_size_quality_background_output_format_and_model
|
|
562
|
+
client = AiLite.new(api_key: "token-abc")
|
|
563
|
+
|
|
564
|
+
with_stubbed_http(image_response) do |captured, _response|
|
|
565
|
+
client.image(
|
|
566
|
+
"A transparent app icon",
|
|
567
|
+
model: "gpt-image-test",
|
|
568
|
+
size: "1024x1536",
|
|
569
|
+
quality: "high",
|
|
570
|
+
background: "transparent",
|
|
571
|
+
output_format: "webp",
|
|
572
|
+
options: {
|
|
573
|
+
moderation: "auto",
|
|
574
|
+
output_compression: 80
|
|
575
|
+
}
|
|
576
|
+
)
|
|
577
|
+
payload = JSON.parse(captured[:http].last_request.body)
|
|
578
|
+
|
|
579
|
+
assert_equal "gpt-image-test", payload["model"]
|
|
580
|
+
assert_equal "A transparent app icon", payload["prompt"]
|
|
581
|
+
assert_equal "1024x1536", payload["size"]
|
|
582
|
+
assert_equal "high", payload["quality"]
|
|
583
|
+
assert_equal "transparent", payload["background"]
|
|
584
|
+
assert_equal "webp", payload["output_format"]
|
|
585
|
+
assert_equal "auto", payload["moderation"]
|
|
586
|
+
assert_equal 80, payload["output_compression"]
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def test_image_uses_class_level_configured_client
|
|
591
|
+
AiLite.configure do |config|
|
|
592
|
+
config.api_key = "configured-key"
|
|
593
|
+
config.image_model = "gpt-image-config"
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
with_stubbed_http(image_response) do |captured, _response|
|
|
597
|
+
AiLite.image("Use configured defaults")
|
|
598
|
+
payload = JSON.parse(captured[:http].last_request.body)
|
|
599
|
+
|
|
600
|
+
assert_equal "gpt-image-config", payload["model"]
|
|
601
|
+
assert_equal "Bearer configured-key", captured[:http].last_request["Authorization"]
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def test_image_writes_decoded_content_to_output_path
|
|
606
|
+
client = AiLite.new(api_key: "token-abc")
|
|
607
|
+
image_data = Base64.strict_encode64("fake image")
|
|
608
|
+
|
|
609
|
+
Tempfile.create(["generated", ".png"]) do |file|
|
|
610
|
+
with_stubbed_http(image_response(b64_json: image_data)) do |_captured, _response|
|
|
611
|
+
result = client.image("A file output", output_path: file.path)
|
|
612
|
+
|
|
613
|
+
assert_equal image_data, result["content"]
|
|
614
|
+
assert_equal "fake image", File.binread(file.path)
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def test_image_debug_true_returns_raw_usage
|
|
620
|
+
client = AiLite.new(api_key: "token-abc")
|
|
621
|
+
image_data = Base64.strict_encode64("fake image")
|
|
622
|
+
|
|
623
|
+
with_stubbed_http(image_response(b64_json: image_data)) do |_captured, _response|
|
|
624
|
+
result = client.image("A debuggable image", debug: true)
|
|
625
|
+
|
|
626
|
+
assert_equal image_data, result["content"]
|
|
627
|
+
assert_equal({ "input_tokens" => 10, "output_tokens" => 20, "total_tokens" => 30 }, result["raw"]["usage"])
|
|
628
|
+
end
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
def test_image_output_path_without_image_data_returns_standard_envelope
|
|
632
|
+
client = AiLite.new(api_key: "token-abc")
|
|
633
|
+
|
|
634
|
+
Tempfile.create(["generated", ".png"]) do |file|
|
|
635
|
+
with_stubbed_http(image_response(b64_json: nil)) do |_captured, _response|
|
|
636
|
+
result = client.image("A missing image", output_path: file.path)
|
|
637
|
+
|
|
638
|
+
assert_nil result["content"]
|
|
639
|
+
assert_nil result["response_id"]
|
|
640
|
+
assert_equal 200, result["status"]
|
|
641
|
+
assert_equal "No image data returned", result["error"]
|
|
642
|
+
assert_nil result["raw"]
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
end
|
|
646
|
+
|
|
528
647
|
def test_http_errors_return_standard_envelope
|
|
529
648
|
client = AiLite.new(api_key: "token-abc")
|
|
530
649
|
body = JSON.generate("error" => { "message" => "Invalid API key" })
|
|
@@ -659,6 +778,26 @@ class AiLiteTest < Minitest::Test
|
|
|
659
778
|
FakeResponse.new("200", body)
|
|
660
779
|
end
|
|
661
780
|
|
|
781
|
+
def image_response(b64_json: Base64.strict_encode64("fake image"))
|
|
782
|
+
image = {}
|
|
783
|
+
image["b64_json"] = b64_json if b64_json
|
|
784
|
+
|
|
785
|
+
body = JSON.generate(
|
|
786
|
+
"created" => 1_713_833_628,
|
|
787
|
+
"background" => "opaque",
|
|
788
|
+
"data" => [image],
|
|
789
|
+
"output_format" => "png",
|
|
790
|
+
"quality" => "medium",
|
|
791
|
+
"size" => "1024x1024",
|
|
792
|
+
"usage" => {
|
|
793
|
+
"input_tokens" => 10,
|
|
794
|
+
"output_tokens" => 20,
|
|
795
|
+
"total_tokens" => 30
|
|
796
|
+
}
|
|
797
|
+
)
|
|
798
|
+
FakeResponse.new("200", body)
|
|
799
|
+
end
|
|
800
|
+
|
|
662
801
|
def with_env(values)
|
|
663
802
|
originals = {}
|
|
664
803
|
|
metadata
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ai-lite
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- William Basmayor
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: AI Lite is a dependency-light Ruby client for Rails apps and plain Ruby
|
|
14
14
|
projects that need simple OpenAI Responses API calls.
|
|
15
|
-
email:
|
|
15
|
+
email:
|
|
16
16
|
executables: []
|
|
17
17
|
extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
|
@@ -27,7 +27,7 @@ homepage: https://github.com/wbasmayor/ai-lite
|
|
|
27
27
|
licenses:
|
|
28
28
|
- MIT
|
|
29
29
|
metadata: {}
|
|
30
|
-
post_install_message:
|
|
30
|
+
post_install_message:
|
|
31
31
|
rdoc_options: []
|
|
32
32
|
require_paths:
|
|
33
33
|
- lib
|
|
@@ -42,8 +42,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
42
42
|
- !ruby/object:Gem::Version
|
|
43
43
|
version: '0'
|
|
44
44
|
requirements: []
|
|
45
|
-
rubygems_version: 3.
|
|
46
|
-
signing_key:
|
|
45
|
+
rubygems_version: 3.5.22
|
|
46
|
+
signing_key:
|
|
47
47
|
specification_version: 4
|
|
48
48
|
summary: Minimal Ruby client for simple AI chat calls through the OpenAI Responses
|
|
49
49
|
API.
|