veida 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +77 -0
- data/lib/veida/version.rb +3 -0
- data/lib/veida.rb +108 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 796b860109aa1b9968cda028bf0b9aa2f4fd925447ac1a00cceb9de8da6a4da1
|
|
4
|
+
data.tar.gz: 8a65df012dea4b908f9299bddc4661c0d4b8a9613283d6a8deeda9f6e075670e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e0c19694c5f867b8edff99a3aed0ee23d0af4ce32b03963ad25efc430a42ebee03edf70f35057262cce10a6e717a2b846cb84643ca077e229878a917e15db34c
|
|
7
|
+
data.tar.gz: 8458eb9395c36156b58832184d525401e6ebeb4bf25927543bdf7106053faa85199846cbf60c10ac1565c813fd7463de1140f26580f87accb5db198ab0142ae6
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Veida (https://veida.ai)
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# veida
|
|
2
|
+
|
|
3
|
+
Generate images from Ruby with **no API key, no account and no card**.
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "veida"
|
|
7
|
+
|
|
8
|
+
img = Veida.generate("a paper boat on a calm lake at sunrise, soft light", aspect_ratio: "16:9")
|
|
9
|
+
puts img.url # https://cdn.veida.ai/uploads/....webp
|
|
10
|
+
puts img.watermarked # true on the free tier
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Install:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gem install veida
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Most image libraries want a key from OpenAI, fal or Replicate before they run once.
|
|
20
|
+
This one talks to the anonymous tier of [Veida](https://veida.ai/?utm_source=rubygems&utm_medium=package), an online AI image generator,
|
|
21
|
+
using a client id the library invents rather than an account you register, so it works on a
|
|
22
|
+
fresh machine with nothing configured.
|
|
23
|
+
|
|
24
|
+
## What the free tier covers
|
|
25
|
+
|
|
26
|
+
Measured against the running service:
|
|
27
|
+
|
|
28
|
+
| | |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Anonymous grant per client id | 4 credits |
|
|
31
|
+
| One text-to-image run | 4 credits, so one image per id |
|
|
32
|
+
| Per-IP ceiling | 30 credits a day, roughly 7 images |
|
|
33
|
+
| Output | 1K, watermarked |
|
|
34
|
+
|
|
35
|
+
The library mints a fresh client id for every call, so a loop is not limited to one image;
|
|
36
|
+
the per-IP ceiling is what stops it. When that is spent you get `Veida::QuotaError` saying so.
|
|
37
|
+
|
|
38
|
+
Editing an existing photo is **not** offered here. The editing model costs 30 credits against
|
|
39
|
+
a 4-credit grant, so an anonymous edit could never be paid for. Signed in, the
|
|
40
|
+
[image editor](https://veida.ai/image/chatgpt-image-editor?utm_source=rubygems&utm_medium=package) does that in a browser.
|
|
41
|
+
|
|
42
|
+
## Past the free tier
|
|
43
|
+
|
|
44
|
+
Signing in removes the watermark, lifts the cap and opens the larger models:
|
|
45
|
+
[GPT Image 2](https://veida.ai/image/gpt-image-2?utm_source=rubygems&utm_medium=package) when the picture must contain readable text,
|
|
46
|
+
[Nano Banana 2](https://veida.ai/image/nano-banana-2?utm_source=rubygems&utm_medium=package) for instruction following and
|
|
47
|
+
[Seedream 5.0 Lite](https://veida.ai/image/seedream-5-lite?utm_source=rubygems&utm_medium=package) for colour and composition.
|
|
48
|
+
What each costs is on the [pricing page](https://veida.ai/pricing?utm_source=rubygems&utm_medium=package).
|
|
49
|
+
|
|
50
|
+
## Prompts that work
|
|
51
|
+
|
|
52
|
+
Name the light, the material and the composition. "A product photo of a mug" gives the model
|
|
53
|
+
nothing; "matte black ceramic mug on pale oak, soft window light from the left, shallow depth
|
|
54
|
+
of field" gives it a picture. Worked examples live in the
|
|
55
|
+
[prompt library](https://veida.ai/image/prompts?utm_source=rubygems&utm_medium=package). The same engine backs the
|
|
56
|
+
[ChatGPT image generator](https://veida.ai/image/chatgpt-image-generator?utm_source=rubygems&utm_medium=package) and the
|
|
57
|
+
[free AI image generator](https://veida.ai/image?utm_source=rubygems&utm_medium=package) in a browser.
|
|
58
|
+
|
|
59
|
+
## API
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
Veida.generate(prompt, aspect_ratio: "1:1", timeout: 240, poll_every: 4) # => Veida::Image(url, watermarked)
|
|
63
|
+
|
|
64
|
+
Veida::QuotaError # wait until tomorrow, or sign in
|
|
65
|
+
Veida::RejectedError # reword the prompt
|
|
66
|
+
Veida::TimeoutError # safe to retry
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Aspect ratio is one of `1:1`, `16:9`, `9:16`, `4:3`, `3:4`.
|
|
70
|
+
|
|
71
|
+
Standard library only (`net/http`, `json`), Ruby 2.6 and up.
|
|
72
|
+
|
|
73
|
+
Other clients: [Python](https://pypi.org/project/veida/) ·
|
|
74
|
+
[Go](https://pkg.go.dev/github.com/hanshs474/veida-go) ·
|
|
75
|
+
[MCP server](https://www.npmjs.com/package/veida-mcp).
|
|
76
|
+
|
|
77
|
+
MIT.
|
data/lib/veida.rb
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "net/http"
|
|
5
|
+
require "securerandom"
|
|
6
|
+
require "uri"
|
|
7
|
+
require_relative "veida/version"
|
|
8
|
+
|
|
9
|
+
# Generate images with no API key and no account.
|
|
10
|
+
#
|
|
11
|
+
# Calls the free tier of https://veida.ai, which meters its allowance against a
|
|
12
|
+
# client id this library invents rather than an account you register.
|
|
13
|
+
# Measured against the running service: 4 credits per id, 4 per image, 30 per
|
|
14
|
+
# IP per day. Free output is 1K and watermarked.
|
|
15
|
+
module Veida
|
|
16
|
+
BASE_URL = "https://veida.ai"
|
|
17
|
+
ANON_GRANT = 4
|
|
18
|
+
COST_TEXT_TO_IMAGE = 4
|
|
19
|
+
IP_DAILY_CEILING = 30
|
|
20
|
+
MODEL = "veida-image-v1"
|
|
21
|
+
ASPECT_RATIOS = %w[1:1 16:9 9:16 4:3 3:4].freeze
|
|
22
|
+
|
|
23
|
+
# One finished picture. +url+ is a permanent CDN link; +watermarked+ says
|
|
24
|
+
# whether a mark was drawn on it.
|
|
25
|
+
Image = Struct.new(:url, :watermarked)
|
|
26
|
+
|
|
27
|
+
# Base error. Rescue the subclass that matches what you want to do next.
|
|
28
|
+
class Error < StandardError; end
|
|
29
|
+
# The free allowance is spent for this machine today. Wait, or sign in.
|
|
30
|
+
class QuotaError < Error; end
|
|
31
|
+
# The content filter refused the prompt. Reword it rather than retrying.
|
|
32
|
+
class RejectedError < Error; end
|
|
33
|
+
# The deadline passed while the job was still queued.
|
|
34
|
+
class TimeoutError < Error; end
|
|
35
|
+
|
|
36
|
+
module_function
|
|
37
|
+
|
|
38
|
+
# A fresh client id. The grant pays for one image, so ids are never reused.
|
|
39
|
+
def anon_id
|
|
40
|
+
"rb-#{SecureRandom.hex(8)}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Turn a prompt into an image. Returns a Veida::Image.
|
|
44
|
+
def generate(prompt, aspect_ratio: "1:1", timeout: 240, poll_every: 4, base_url: BASE_URL)
|
|
45
|
+
raise ArgumentError, "prompt is required" if prompt.to_s.strip.empty?
|
|
46
|
+
unless ASPECT_RATIOS.include?(aspect_ratio)
|
|
47
|
+
raise ArgumentError, "aspect_ratio must be one of #{ASPECT_RATIOS.join(', ')}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
id = anon_id
|
|
51
|
+
started = Time.now
|
|
52
|
+
submitted = request(:post, "#{base_url}/api/ai/generate", id, {
|
|
53
|
+
provider: "kie", mediaType: "image", model: MODEL, scene: "text-to-image",
|
|
54
|
+
prompt: prompt, options: { aspect_ratio: aspect_ratio }
|
|
55
|
+
})
|
|
56
|
+
task = parse_submit(submitted)
|
|
57
|
+
|
|
58
|
+
loop do
|
|
59
|
+
raise TimeoutError, "still queued after #{timeout}s" if Time.now - started > timeout
|
|
60
|
+
sleep poll_every
|
|
61
|
+
polled = begin
|
|
62
|
+
request(:get, "#{base_url}/api/ai/anon-query?taskId=#{URI.encode_www_form_component(task)}&provider=kie&mediaType=image", id)
|
|
63
|
+
rescue StandardError
|
|
64
|
+
next # a dropped connection mid-queue is not a failed generation
|
|
65
|
+
end
|
|
66
|
+
image = parse_poll(polled)
|
|
67
|
+
return image if image
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The quota wall answers HTTP 200 with code 0, so it is recognised by a field.
|
|
72
|
+
def parse_submit(env)
|
|
73
|
+
raise Error, (env["message"] || "request failed") unless env["code"].to_i.zero?
|
|
74
|
+
data = env["data"] || {}
|
|
75
|
+
if data["wall"]
|
|
76
|
+
if data["reason"] == "anon_ip_daily"
|
|
77
|
+
raise QuotaError, "this machine has used its #{IP_DAILY_CEILING} free credits for today; sign in at https://veida.ai/pricing"
|
|
78
|
+
end
|
|
79
|
+
raise QuotaError, "free allowance spent (#{data['reason']}); sign in at https://veida.ai/pricing"
|
|
80
|
+
end
|
|
81
|
+
id = data["id"].to_s
|
|
82
|
+
raise Error, "the service returned no task id" if id.empty?
|
|
83
|
+
id
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Status is not monotonic, so only a terminal failure ends the wait early.
|
|
87
|
+
def parse_poll(env)
|
|
88
|
+
data = env["data"] || {}
|
|
89
|
+
url = Array(data["images"]).first
|
|
90
|
+
return Image.new(url, Array(data["watermarked"]).first == true) if url
|
|
91
|
+
if %w[failed error].include?(data["status"].to_s.downcase)
|
|
92
|
+
raise RejectedError, "the prompt was refused by the content filter; reword it"
|
|
93
|
+
end
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def request(method, url, id, body = nil)
|
|
98
|
+
uri = URI(url)
|
|
99
|
+
req = method == :post ? Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
|
|
100
|
+
req["Content-Type"] = "application/json"
|
|
101
|
+
req["x-anon-id"] = id
|
|
102
|
+
req["User-Agent"] = "veida-rb/#{VERSION}"
|
|
103
|
+
req.body = JSON.generate(body) if body
|
|
104
|
+
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 60) { |h| h.request(req) }
|
|
105
|
+
JSON.parse(res.body)
|
|
106
|
+
end
|
|
107
|
+
private_class_method :request
|
|
108
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: veida
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Veida
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-10-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: 'A zero-dependency client for the free tier of veida.ai: text-to-image
|
|
14
|
+
without an API key, an account or a card.'
|
|
15
|
+
email:
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- LICENSE
|
|
21
|
+
- README.md
|
|
22
|
+
- lib/veida.rb
|
|
23
|
+
- lib/veida/version.rb
|
|
24
|
+
homepage: https://veida.ai
|
|
25
|
+
licenses:
|
|
26
|
+
- MIT
|
|
27
|
+
metadata:
|
|
28
|
+
homepage_uri: https://veida.ai
|
|
29
|
+
source_code_uri: https://github.com/hanshs474/veida-rb
|
|
30
|
+
documentation_uri: https://veida.ai/image
|
|
31
|
+
bug_tracker_uri: https://github.com/hanshs474/veida-rb/issues
|
|
32
|
+
rubygems_mfa_required: 'false'
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '2.6'
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
requirements: []
|
|
48
|
+
rubygems_version: 3.0.3.1
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: Generate images from Ruby with no API key and no account
|
|
52
|
+
test_files: []
|