kavel 0.1.0__tar.gz

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.
kavel-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: kavel
3
+ Version: 0.1.0
4
+ Summary: Generate images from a prompt with no API key and no account
5
+ Author: hanshs474
6
+ License: MIT
7
+ Project-URL: Homepage, https://www.kavel.ai/?ref=pypi
8
+ Project-URL: Image tools, https://www.kavel.ai/image?ref=pypi
9
+ Project-URL: Video tools, https://www.kavel.ai/video?ref=pypi
10
+ Project-URL: Source, https://github.com/hanshs474/kavel-image-skill
11
+ Keywords: ai,image-generation,text-to-image,image-editing,no-api-key
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Multimedia :: Graphics
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+
20
+ # kavel
21
+
22
+ Generate images from a prompt with **no API key and no account**.
23
+
24
+ ```python
25
+ from kavel import generate
26
+
27
+ url = generate("a paper-cut layered mountain range at dusk, warm rim light")
28
+ ```
29
+
30
+ That is the whole setup. No key to fetch, no config file, no dependencies —
31
+ standard library only. It calls the anonymous endpoint on
32
+ [Kavel](https://www.kavel.ai/?ref=pypi), an online AI image and video studio.
33
+
34
+ ## Editing a photo
35
+
36
+ Pass `image_url` and the prompt describes the change rather than the picture:
37
+
38
+ ```python
39
+ url = generate(
40
+ "restyle the hair into a shoulder-length layered cut, "
41
+ "keep the same face, skin and lighting",
42
+ image_url="https://example.com/portrait.jpg",
43
+ )
44
+ ```
45
+
46
+ Naming what must stay is what holds the likeness. Prompts that only name the
47
+ edit tend to drift the whole face.
48
+
49
+ ## What the free tier actually covers
50
+
51
+ | | |
52
+ |---|---|
53
+ | Anonymous grant | 15 credits |
54
+ | Text-to-image | 5 credits — so the grant covers two |
55
+ | Photo edit (`nano-banana-2-lite`) | 15 credits — exactly one |
56
+ | Per-IP ceiling | 30 credits a day |
57
+
58
+ Anonymous output is 1K and watermarked. Signing in on
59
+ [kavel.ai](https://www.kavel.ai/?ref=pypi) removes the watermark and opens the
60
+ larger models.
61
+
62
+ **Video is deliberately not exposed here.** The cheapest clip is 40 credits
63
+ against a 15-credit anonymous wallet, so a signed-out call cannot succeed at
64
+ any setting. Signed-out video on the site runs on a browser-side engine
65
+ instead — see [the video tools](https://www.kavel.ai/video?ref=pypi).
66
+
67
+ ## Errors worth catching
68
+
69
+ `KavelError` covers the three things that actually happen:
70
+
71
+ - the content filter refused the prompt (credits are spent either way, so
72
+ rewrite rather than retry)
73
+ - the queue ran past `timeout`
74
+ - the per-IP daily ceiling returned 429
75
+
76
+ ## Prompts that work
77
+
78
+ Kavel keeps a page per edit, each preloaded with a prompt for that specific
79
+ job — [hairstyle](https://www.kavel.ai/image/ai-hairstyle-changer?ref=pypi),
80
+ [outfit](https://www.kavel.ai/image/ai-outfit-generator?ref=pypi),
81
+ and the rest under [image tools](https://www.kavel.ai/image?ref=pypi).
82
+
83
+ MIT licensed.
kavel-0.1.0/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # kavel
2
+
3
+ Generate images from a prompt with **no API key and no account**.
4
+
5
+ ```python
6
+ from kavel import generate
7
+
8
+ url = generate("a paper-cut layered mountain range at dusk, warm rim light")
9
+ ```
10
+
11
+ That is the whole setup. No key to fetch, no config file, no dependencies —
12
+ standard library only. It calls the anonymous endpoint on
13
+ [Kavel](https://www.kavel.ai/?ref=pypi), an online AI image and video studio.
14
+
15
+ ## Editing a photo
16
+
17
+ Pass `image_url` and the prompt describes the change rather than the picture:
18
+
19
+ ```python
20
+ url = generate(
21
+ "restyle the hair into a shoulder-length layered cut, "
22
+ "keep the same face, skin and lighting",
23
+ image_url="https://example.com/portrait.jpg",
24
+ )
25
+ ```
26
+
27
+ Naming what must stay is what holds the likeness. Prompts that only name the
28
+ edit tend to drift the whole face.
29
+
30
+ ## What the free tier actually covers
31
+
32
+ | | |
33
+ |---|---|
34
+ | Anonymous grant | 15 credits |
35
+ | Text-to-image | 5 credits — so the grant covers two |
36
+ | Photo edit (`nano-banana-2-lite`) | 15 credits — exactly one |
37
+ | Per-IP ceiling | 30 credits a day |
38
+
39
+ Anonymous output is 1K and watermarked. Signing in on
40
+ [kavel.ai](https://www.kavel.ai/?ref=pypi) removes the watermark and opens the
41
+ larger models.
42
+
43
+ **Video is deliberately not exposed here.** The cheapest clip is 40 credits
44
+ against a 15-credit anonymous wallet, so a signed-out call cannot succeed at
45
+ any setting. Signed-out video on the site runs on a browser-side engine
46
+ instead — see [the video tools](https://www.kavel.ai/video?ref=pypi).
47
+
48
+ ## Errors worth catching
49
+
50
+ `KavelError` covers the three things that actually happen:
51
+
52
+ - the content filter refused the prompt (credits are spent either way, so
53
+ rewrite rather than retry)
54
+ - the queue ran past `timeout`
55
+ - the per-IP daily ceiling returned 429
56
+
57
+ ## Prompts that work
58
+
59
+ Kavel keeps a page per edit, each preloaded with a prompt for that specific
60
+ job — [hairstyle](https://www.kavel.ai/image/ai-hairstyle-changer?ref=pypi),
61
+ [outfit](https://www.kavel.ai/image/ai-outfit-generator?ref=pypi),
62
+ and the rest under [image tools](https://www.kavel.ai/image?ref=pypi).
63
+
64
+ MIT licensed.
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "kavel"
7
+ version = "0.1.0"
8
+ description = "Generate images from a prompt with no API key and no account"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "hanshs474" }]
13
+ keywords = ["ai", "image-generation", "text-to-image", "image-editing", "no-api-key"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Multimedia :: Graphics",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://www.kavel.ai/?ref=pypi"
24
+ "Image tools" = "https://www.kavel.ai/image?ref=pypi"
25
+ "Video tools" = "https://www.kavel.ai/video?ref=pypi"
26
+ Source = "https://github.com/hanshs474/kavel-image-skill"
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
kavel-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,132 @@
1
+ """Generate images from a prompt with no API key and no account.
2
+
3
+ Calls the anonymous endpoint on kavel.ai — https://www.kavel.ai/?ref=pypi
4
+
5
+ from kavel import generate
6
+ url = generate("a paper-cut layered mountain range at dusk")
7
+
8
+ Standard library only. No dependencies, no configuration.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import time
15
+ import urllib.error
16
+ import urllib.request
17
+ import uuid
18
+
19
+ __version__ = "0.1.0"
20
+ __all__ = ["generate", "KavelError", "BASE"]
21
+
22
+ BASE = "https://www.kavel.ai"
23
+
24
+ #: Anonymous wallet, measured against the live site on 2026-09-04.
25
+ #: A text-to-image run costs 5 of the 15 credits a fresh visitor is granted;
26
+ #: an edit (``nano-banana-2-lite``) costs all 15. There is also a per-IP
27
+ #: ceiling of 30 credits a day. Video cannot be paid for anonymously at any
28
+ #: setting, which is why this module does not expose it.
29
+ ANON_GRANT = 15
30
+ COST_TEXT_TO_IMAGE = 5
31
+ COST_EDIT = 15
32
+
33
+
34
+ class KavelError(RuntimeError):
35
+ """Raised when a generation is refused, times out, or comes back empty."""
36
+
37
+
38
+ def _post(path: str, payload: dict, anon: str) -> dict:
39
+ req = urllib.request.Request(
40
+ BASE + path,
41
+ data=json.dumps(payload).encode(),
42
+ headers={
43
+ "Content-Type": "application/json",
44
+ "x-anon-id": anon,
45
+ "User-Agent": f"kavel-python/{__version__}",
46
+ },
47
+ )
48
+ try:
49
+ with urllib.request.urlopen(req, timeout=60) as r:
50
+ return json.loads(r.read())
51
+ except urllib.error.HTTPError as e:
52
+ if e.code == 429:
53
+ raise KavelError(
54
+ "per-IP daily ceiling reached (30 credits); wait or sign in"
55
+ ) from e
56
+ raise KavelError(f"HTTP {e.code}: {e.read()[:200]!r}") from e
57
+
58
+
59
+ def _get(path: str, anon: str) -> dict:
60
+ req = urllib.request.Request(
61
+ BASE + path,
62
+ headers={"x-anon-id": anon, "User-Agent": f"kavel-python/{__version__}"},
63
+ )
64
+ with urllib.request.urlopen(req, timeout=60) as r:
65
+ return json.loads(r.read())
66
+
67
+
68
+ def generate(
69
+ prompt: str,
70
+ *,
71
+ aspect_ratio: str = "1:1",
72
+ image_url: str | None = None,
73
+ timeout: float = 240.0,
74
+ poll_every: float = 4.0,
75
+ ) -> str:
76
+ """Generate one image and return its CDN URL.
77
+
78
+ ``prompt`` describes the picture. Pass ``image_url`` to edit an existing
79
+ photo instead of generating from scratch — the prompt then describes the
80
+ change and should name what must stay ("keep the same face and lighting"),
81
+ because anything left unsaid is fair game for the model.
82
+
83
+ Editing runs on ``nano-banana-2-lite`` and costs the whole anonymous grant,
84
+ so a signed-out caller gets one edit. Text-to-image runs on
85
+ ``kavel-image-v1``, which takes no image input.
86
+
87
+ Raises :class:`KavelError` on refusal, timeout, or an empty result.
88
+ """
89
+ anon = f"anon-{uuid.uuid4()}"
90
+ model = "nano-banana-2-lite" if image_url else "kavel-image-v1"
91
+ scene = "image-to-image" if image_url else "text-to-image"
92
+ options: dict = {"aspect_ratio": aspect_ratio}
93
+ if image_url:
94
+ options["imageUrl"] = image_url
95
+
96
+ submitted = _post(
97
+ "/api/ai/generate",
98
+ {
99
+ "provider": "kie",
100
+ "mediaType": "image",
101
+ "model": model,
102
+ "scene": scene,
103
+ "prompt": prompt,
104
+ "options": options,
105
+ },
106
+ anon,
107
+ )
108
+ task_id = (submitted.get("data") or {}).get("id")
109
+ if not task_id:
110
+ raise KavelError(f"no task id in response: {str(submitted)[:200]}")
111
+
112
+ deadline = time.monotonic() + timeout
113
+ while time.monotonic() < deadline:
114
+ time.sleep(poll_every)
115
+ data = _get(
116
+ f"/api/ai/anon-query?taskId={task_id}"
117
+ "&provider=kie&mediaType=image",
118
+ anon,
119
+ ).get("data") or {}
120
+ images = data.get("images") or []
121
+ if images:
122
+ return images[0]
123
+ status = str(data.get("status") or "").lower()
124
+ if status in {"failed", "error"}:
125
+ # A finished task with no image is the content filter, and the
126
+ # credits are already spent — retrying the same text spends them
127
+ # again for the same answer.
128
+ raise KavelError(
129
+ "generation refused (content filter); rewrite the prompt "
130
+ "rather than retrying it"
131
+ )
132
+ raise KavelError(f"timed out after {timeout:g}s; the queue can run long")
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: kavel
3
+ Version: 0.1.0
4
+ Summary: Generate images from a prompt with no API key and no account
5
+ Author: hanshs474
6
+ License: MIT
7
+ Project-URL: Homepage, https://www.kavel.ai/?ref=pypi
8
+ Project-URL: Image tools, https://www.kavel.ai/image?ref=pypi
9
+ Project-URL: Video tools, https://www.kavel.ai/video?ref=pypi
10
+ Project-URL: Source, https://github.com/hanshs474/kavel-image-skill
11
+ Keywords: ai,image-generation,text-to-image,image-editing,no-api-key
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Multimedia :: Graphics
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+
20
+ # kavel
21
+
22
+ Generate images from a prompt with **no API key and no account**.
23
+
24
+ ```python
25
+ from kavel import generate
26
+
27
+ url = generate("a paper-cut layered mountain range at dusk, warm rim light")
28
+ ```
29
+
30
+ That is the whole setup. No key to fetch, no config file, no dependencies —
31
+ standard library only. It calls the anonymous endpoint on
32
+ [Kavel](https://www.kavel.ai/?ref=pypi), an online AI image and video studio.
33
+
34
+ ## Editing a photo
35
+
36
+ Pass `image_url` and the prompt describes the change rather than the picture:
37
+
38
+ ```python
39
+ url = generate(
40
+ "restyle the hair into a shoulder-length layered cut, "
41
+ "keep the same face, skin and lighting",
42
+ image_url="https://example.com/portrait.jpg",
43
+ )
44
+ ```
45
+
46
+ Naming what must stay is what holds the likeness. Prompts that only name the
47
+ edit tend to drift the whole face.
48
+
49
+ ## What the free tier actually covers
50
+
51
+ | | |
52
+ |---|---|
53
+ | Anonymous grant | 15 credits |
54
+ | Text-to-image | 5 credits — so the grant covers two |
55
+ | Photo edit (`nano-banana-2-lite`) | 15 credits — exactly one |
56
+ | Per-IP ceiling | 30 credits a day |
57
+
58
+ Anonymous output is 1K and watermarked. Signing in on
59
+ [kavel.ai](https://www.kavel.ai/?ref=pypi) removes the watermark and opens the
60
+ larger models.
61
+
62
+ **Video is deliberately not exposed here.** The cheapest clip is 40 credits
63
+ against a 15-credit anonymous wallet, so a signed-out call cannot succeed at
64
+ any setting. Signed-out video on the site runs on a browser-side engine
65
+ instead — see [the video tools](https://www.kavel.ai/video?ref=pypi).
66
+
67
+ ## Errors worth catching
68
+
69
+ `KavelError` covers the three things that actually happen:
70
+
71
+ - the content filter refused the prompt (credits are spent either way, so
72
+ rewrite rather than retry)
73
+ - the queue ran past `timeout`
74
+ - the per-IP daily ceiling returned 429
75
+
76
+ ## Prompts that work
77
+
78
+ Kavel keeps a page per edit, each preloaded with a prompt for that specific
79
+ job — [hairstyle](https://www.kavel.ai/image/ai-hairstyle-changer?ref=pypi),
80
+ [outfit](https://www.kavel.ai/image/ai-outfit-generator?ref=pypi),
81
+ and the rest under [image tools](https://www.kavel.ai/image?ref=pypi).
82
+
83
+ MIT licensed.
@@ -0,0 +1,7 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/kavel/__init__.py
4
+ src/kavel.egg-info/PKG-INFO
5
+ src/kavel.egg-info/SOURCES.txt
6
+ src/kavel.egg-info/dependency_links.txt
7
+ src/kavel.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ kavel