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.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +77 -0
  4. data/lib/veida/version.rb +3 -0
  5. data/lib/veida.rb +108 -0
  6. 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.
@@ -0,0 +1,3 @@
1
+ module Veida
2
+ VERSION = "0.1.0"
3
+ end
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: []