runapi-z-image 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.
@@ -0,0 +1,29 @@
1
+ # Build artifacts
2
+ dist/
3
+ build/
4
+ *.egg-info/
5
+ *.egg
6
+
7
+ # Bytecode
8
+ __pycache__/
9
+ *.py[cod]
10
+
11
+ # Virtual environments
12
+ .venv/
13
+ venv/
14
+
15
+ # uv
16
+ uv.lock
17
+
18
+ # Test / type caches
19
+ .pytest_cache/
20
+ .mypy_cache/
21
+ .ruff_cache/
22
+ .coverage
23
+ htmlcov/
24
+
25
+ # IDE / OS
26
+ .idea/
27
+ .vscode/
28
+ *.swp
29
+ .DS_Store
@@ -0,0 +1,80 @@
1
+ Metadata-Version: 2.4
2
+ Name: runapi-z-image
3
+ Version: 0.1.0
4
+ Summary: Z-Image text-to-image client for RunAPI
5
+ Project-URL: Homepage, https://runapi.ai/models/z-image
6
+ Project-URL: Documentation, https://runapi.ai/docs#sdk-z-image
7
+ Author-email: RunAPI <contact@runapi.ai>
8
+ License-Expression: Apache-2.0
9
+ Keywords: ai,runapi,sdk,text-to-image,z-image
10
+ Requires-Python: >=3.9
11
+ Requires-Dist: runapi-core
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Z-Image Python SDK for RunAPI
15
+
16
+ The Z-Image Python SDK is the language-specific package for Z-Image on RunAPI.
17
+ Use it for text-to-image flows when your application needs JSON request bodies,
18
+ task status lookup, and consistent RunAPI errors in Python.
19
+
20
+ For model details, use https://runapi.ai/models/z-image; for API reference, use
21
+ https://runapi.ai/docs#z-image; for SDK docs, use https://runapi.ai/docs#sdk-z-image.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install runapi-z-image
27
+ ```
28
+
29
+ ## Quick start
30
+
31
+ ```python
32
+ from runapi.z_image import ZImageClient
33
+
34
+ client = ZImageClient() # reads RUNAPI_API_KEY, or pass api_key="sk-..."
35
+
36
+ task = client.text_to_image.create(
37
+ model="z-image",
38
+ prompt="A neon city street after rain, cinematic",
39
+ aspect_ratio="16:9",
40
+ )
41
+ status = client.text_to_image.get(task.id)
42
+ ```
43
+
44
+ Use `create` to submit a task and return quickly, `get` to fetch the latest task
45
+ state, and `run` to create and poll until completion:
46
+
47
+ ```python
48
+ result = client.text_to_image.run(
49
+ model="z-image",
50
+ prompt="A serene mountain lake at dawn",
51
+ aspect_ratio="1:1",
52
+ )
53
+ print(result.images[0].url)
54
+ ```
55
+
56
+ In web request handlers, prefer `create` plus webhook or later `get` polling so a
57
+ worker is not held open.
58
+
59
+ RunAPI-generated file URLs are temporary. Download and store generated images in
60
+ your own durable storage within 7 days; do not treat returned URLs as long-term
61
+ assets.
62
+
63
+ ## Language notes
64
+
65
+ Pass parameters as keyword arguments and catch the `runapi.z_image` error
66
+ classes when building image jobs or scripts. The available resource is
67
+ `text_to_image`. Keep `RUNAPI_API_KEY` in the environment or your secret
68
+ manager; never commit API keys or callback secrets.
69
+
70
+ ## Links
71
+
72
+ - Model page: https://runapi.ai/models/z-image
73
+ - SDK docs: https://runapi.ai/docs#sdk-z-image
74
+ - Product docs: https://runapi.ai/docs#z-image
75
+ - Pricing and rate limits: https://runapi.ai/models/z-image
76
+ - Full catalog: https://runapi.ai/models
77
+
78
+ ## License
79
+
80
+ Licensed under the Apache License, Version 2.0.
@@ -0,0 +1,67 @@
1
+ # Z-Image Python SDK for RunAPI
2
+
3
+ The Z-Image Python SDK is the language-specific package for Z-Image on RunAPI.
4
+ Use it for text-to-image flows when your application needs JSON request bodies,
5
+ task status lookup, and consistent RunAPI errors in Python.
6
+
7
+ For model details, use https://runapi.ai/models/z-image; for API reference, use
8
+ https://runapi.ai/docs#z-image; for SDK docs, use https://runapi.ai/docs#sdk-z-image.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ pip install runapi-z-image
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ```python
19
+ from runapi.z_image import ZImageClient
20
+
21
+ client = ZImageClient() # reads RUNAPI_API_KEY, or pass api_key="sk-..."
22
+
23
+ task = client.text_to_image.create(
24
+ model="z-image",
25
+ prompt="A neon city street after rain, cinematic",
26
+ aspect_ratio="16:9",
27
+ )
28
+ status = client.text_to_image.get(task.id)
29
+ ```
30
+
31
+ Use `create` to submit a task and return quickly, `get` to fetch the latest task
32
+ state, and `run` to create and poll until completion:
33
+
34
+ ```python
35
+ result = client.text_to_image.run(
36
+ model="z-image",
37
+ prompt="A serene mountain lake at dawn",
38
+ aspect_ratio="1:1",
39
+ )
40
+ print(result.images[0].url)
41
+ ```
42
+
43
+ In web request handlers, prefer `create` plus webhook or later `get` polling so a
44
+ worker is not held open.
45
+
46
+ RunAPI-generated file URLs are temporary. Download and store generated images in
47
+ your own durable storage within 7 days; do not treat returned URLs as long-term
48
+ assets.
49
+
50
+ ## Language notes
51
+
52
+ Pass parameters as keyword arguments and catch the `runapi.z_image` error
53
+ classes when building image jobs or scripts. The available resource is
54
+ `text_to_image`. Keep `RUNAPI_API_KEY` in the environment or your secret
55
+ manager; never commit API keys or callback secrets.
56
+
57
+ ## Links
58
+
59
+ - Model page: https://runapi.ai/models/z-image
60
+ - SDK docs: https://runapi.ai/docs#sdk-z-image
61
+ - Product docs: https://runapi.ai/docs#z-image
62
+ - Pricing and rate limits: https://runapi.ai/models/z-image
63
+ - Full catalog: https://runapi.ai/models
64
+
65
+ ## License
66
+
67
+ Licensed under the Apache License, Version 2.0.
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "runapi-z-image"
7
+ version = "0.1.0"
8
+ description = "Z-Image text-to-image client for RunAPI"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "Apache-2.0"
12
+ authors = [{ name = "RunAPI", email = "contact@runapi.ai" }]
13
+ keywords = ["runapi", "z-image", "text-to-image", "ai", "sdk"]
14
+ dependencies = ["runapi-core"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://runapi.ai/models/z-image"
18
+ Documentation = "https://runapi.ai/docs#sdk-z-image"
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["src/runapi"]
22
+
23
+ [tool.uv]
24
+ package = true
25
+
26
+ [tool.uv.sources]
27
+ runapi-core = { workspace = true }
28
+
29
+ [dependency-groups]
30
+ dev = ["pytest>=8"]
31
+
32
+ [tool.runapi]
33
+ slug = "z-image"
@@ -0,0 +1,24 @@
1
+ """Z-Image client for RunAPI."""
2
+
3
+ from runapi.core import (
4
+ AuthenticationError,
5
+ InsufficientCreditsError,
6
+ NotFoundError,
7
+ RateLimitError,
8
+ TaskFailedError,
9
+ TaskTimeoutError,
10
+ ValidationError,
11
+ )
12
+
13
+ from .client import ZImageClient
14
+
15
+ __all__ = [
16
+ "ZImageClient",
17
+ "AuthenticationError",
18
+ "RateLimitError",
19
+ "InsufficientCreditsError",
20
+ "NotFoundError",
21
+ "ValidationError",
22
+ "TaskFailedError",
23
+ "TaskTimeoutError",
24
+ ]
@@ -0,0 +1,27 @@
1
+ """Z-Image client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Optional
6
+
7
+ from runapi.core import ClientOptions, HttpClient, resolve_api_key
8
+
9
+ from .resources.text_to_image import TextToImage
10
+
11
+
12
+ class ZImageClient:
13
+ """Z-Image text-to-image client.
14
+
15
+ Example::
16
+
17
+ client = ZImageClient(api_key="sk-...")
18
+ result = client.text_to_image.run(
19
+ model="z-image", prompt="A neon city street", aspect_ratio="1:1"
20
+ )
21
+ """
22
+
23
+ def __init__(self, api_key: Optional[str] = None, **options: Any) -> None:
24
+ resolved_api_key = resolve_api_key(api_key)
25
+ client_options = ClientOptions(api_key=resolved_api_key, **options)
26
+ http = client_options.http_client or HttpClient(client_options)
27
+ self.text_to_image = TextToImage(http)
@@ -0,0 +1,19 @@
1
+ CONTRACT = {
2
+ "text-to-image": {
3
+ "models": ["z-image"],
4
+ "fields_by_model": {
5
+ "z-image": {
6
+ "aspect_ratio": {
7
+ "enum": ["1:1", "4:3", "3:4", "16:9", "9:16"],
8
+ "required": True
9
+ },
10
+ "prompt": {
11
+ "required": True,
12
+ "min": 1,
13
+ "max": 1000,
14
+ "length": True
15
+ }
16
+ }
17
+ }
18
+ }
19
+ }
File without changes
@@ -0,0 +1,3 @@
1
+ from .text_to_image import TextToImage
2
+
3
+ __all__ = ["TextToImage"]
@@ -0,0 +1,58 @@
1
+ """Z-Image text-to-image resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from runapi.core import Resource
8
+
9
+ from ..contract_gen import CONTRACT
10
+ from ..types import (
11
+ CompletedTextToImageResponse,
12
+ TextToImageResponse,
13
+ )
14
+
15
+
16
+ class TextToImage(Resource):
17
+ """Generate images from text prompts with Z-Image."""
18
+
19
+ ENDPOINT = "/api/v1/z_image/text_to_image"
20
+
21
+ RESPONSE_CLASS = TextToImageResponse
22
+ COMPLETED_RESPONSE_CLASS = CompletedTextToImageResponse
23
+
24
+ def run(self, **params: Any) -> Any:
25
+ """Create a text-to-image task and poll until it completes.
26
+
27
+ Args:
28
+ **params: text-to-image parameters (model, ...).
29
+
30
+ Returns:
31
+ The completed (narrowed) text-to-image response.
32
+ """
33
+ task = self.create(**params)
34
+ return self._poll_until_complete(lambda: self.get(task.id))
35
+
36
+ def create(self, **params: Any) -> Any:
37
+ """Create a text-to-image task and return immediately with an id.
38
+
39
+ Args:
40
+ **params: text-to-image parameters (model, ...).
41
+
42
+ Returns:
43
+ The task creation result with an id.
44
+ """
45
+ compacted = self._compact_params(params)
46
+ self._validate_contract(CONTRACT["text-to-image"], compacted)
47
+ return self._request("post", self.ENDPOINT, body=compacted)
48
+
49
+ def get(self, id: str) -> Any:
50
+ """Fetch the current status of a text-to-image task.
51
+
52
+ Args:
53
+ id: The task id returned by ``create``.
54
+
55
+ Returns:
56
+ The current task status.
57
+ """
58
+ return self._request("get", f"{self.ENDPOINT}/{id}")
@@ -0,0 +1,24 @@
1
+ """Z-Image response models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from runapi.core import BaseModel, TaskResponse, optional, required
6
+
7
+
8
+ class Image(BaseModel):
9
+ url = optional(str)
10
+
11
+
12
+ class TextToImageResponse(TaskResponse):
13
+ """Z-Image text-to-image task status response."""
14
+
15
+ id = required(str)
16
+ status = optional(str, enum=lambda: TaskResponse.Status.ALL)
17
+ images = optional([lambda: Image])
18
+ error = optional(str)
19
+
20
+
21
+ class CompletedTextToImageResponse(TextToImageResponse):
22
+ """Narrowed response from ``run()`` once polling observes completion."""
23
+
24
+ images = required([lambda: Image])
@@ -0,0 +1,128 @@
1
+ import pytest
2
+
3
+ from runapi.core import config
4
+ from runapi.core.errors import AuthenticationError, ValidationError
5
+ from runapi.z_image import ZImageClient
6
+ from runapi.z_image.resources.text_to_image import TextToImage
7
+ from runapi.z_image.types import CompletedTextToImageResponse, TextToImageResponse
8
+
9
+
10
+ class FakeHttp:
11
+ def __init__(self, *responses):
12
+ self._responses = list(responses)
13
+ self.calls = []
14
+
15
+ def request(self, method, path, body=None, options=None):
16
+ self.calls.append((method, path, body))
17
+ if self._responses:
18
+ return self._responses.pop(0)
19
+ return {"id": "task_1", "status": "pending"}
20
+
21
+
22
+ @pytest.fixture(autouse=True)
23
+ def reset_config(monkeypatch):
24
+ monkeypatch.delenv("RUNAPI_API_KEY", raising=False)
25
+ monkeypatch.setattr(config, "api_key", None)
26
+ yield
27
+
28
+
29
+ # --- auth -----------------------------------------------------------------
30
+
31
+
32
+ def test_accepts_api_key_parameter():
33
+ assert isinstance(ZImageClient(api_key="k", http_client=FakeHttp()), ZImageClient)
34
+
35
+
36
+ def test_falls_back_to_global(monkeypatch):
37
+ monkeypatch.setattr(config, "api_key", "global-key")
38
+ assert isinstance(ZImageClient(http_client=FakeHttp()), ZImageClient)
39
+
40
+
41
+ def test_falls_back_to_env(monkeypatch):
42
+ monkeypatch.setenv("RUNAPI_API_KEY", "env-key")
43
+ assert isinstance(ZImageClient(http_client=FakeHttp()), ZImageClient)
44
+
45
+
46
+ def test_raises_without_api_key():
47
+ with pytest.raises(AuthenticationError, match="API key is required"):
48
+ ZImageClient()
49
+
50
+
51
+ # --- injection / accessors ------------------------------------------------
52
+
53
+
54
+ def test_uses_injected_http_client():
55
+ fake = FakeHttp()
56
+ client = ZImageClient(api_key="k", http_client=fake)
57
+ assert client.text_to_image._http is fake
58
+
59
+
60
+ def test_exposes_resource_accessors():
61
+ client = ZImageClient(api_key="k", http_client=FakeHttp())
62
+ assert isinstance(client.text_to_image, TextToImage)
63
+
64
+
65
+ # --- request shapes -------------------------------------------------------
66
+
67
+
68
+ def test_create_posts_compacted_body():
69
+ fake = FakeHttp({"id": "t1", "status": "pending"})
70
+ client = ZImageClient(api_key="k", http_client=fake)
71
+ result = client.text_to_image.create(
72
+ model="z-image", prompt="hello world", aspect_ratio="1:1", seed=None
73
+ )
74
+ assert fake.calls == [
75
+ ("post", "/api/v1/z_image/text_to_image", {"model": "z-image", "prompt": "hello world", "aspect_ratio": "1:1"}),
76
+ ]
77
+ assert isinstance(result, TextToImageResponse)
78
+
79
+
80
+ def test_get_fetches_by_id():
81
+ fake = FakeHttp({"id": "t1", "status": "processing"})
82
+ client = ZImageClient(api_key="k", http_client=fake)
83
+ client.text_to_image.get("t1")
84
+ assert fake.calls == [("get", "/api/v1/z_image/text_to_image/t1", None)]
85
+
86
+
87
+ def test_run_narrows_completed_type():
88
+ fake = FakeHttp(
89
+ {"id": "t1", "status": "pending"},
90
+ {"id": "t1", "status": "completed", "images": [{"url": "https://x/y.png"}]},
91
+ )
92
+ client = ZImageClient(api_key="k", http_client=fake)
93
+ result = client.text_to_image.run(model="z-image", prompt="a serene lake", aspect_ratio="1:1")
94
+ assert isinstance(result, CompletedTextToImageResponse)
95
+ assert result.images[0].url == "https://x/y.png"
96
+
97
+
98
+ # --- validation -----------------------------------------------------------
99
+
100
+
101
+ def test_requires_model():
102
+ client = ZImageClient(api_key="k", http_client=FakeHttp())
103
+ with pytest.raises(ValidationError, match="model must be one of: z-image"):
104
+ client.text_to_image.create(prompt="hi there", aspect_ratio="1:1")
105
+
106
+
107
+ def test_requires_prompt():
108
+ client = ZImageClient(api_key="k", http_client=FakeHttp())
109
+ with pytest.raises(ValidationError, match="prompt is required"):
110
+ client.text_to_image.create(model="z-image", aspect_ratio="1:1")
111
+
112
+
113
+ def test_requires_aspect_ratio():
114
+ client = ZImageClient(api_key="k", http_client=FakeHttp())
115
+ with pytest.raises(ValidationError, match="aspect_ratio is required"):
116
+ client.text_to_image.create(model="z-image", prompt="hi there")
117
+
118
+
119
+ def test_rejects_unknown_model():
120
+ client = ZImageClient(api_key="k", http_client=FakeHttp())
121
+ with pytest.raises(ValidationError, match="model must be one of: z-image"):
122
+ client.text_to_image.create(model="nope", prompt="hi there", aspect_ratio="1:1")
123
+
124
+
125
+ def test_rejects_invalid_aspect_ratio():
126
+ client = ZImageClient(api_key="k", http_client=FakeHttp())
127
+ with pytest.raises(ValidationError, match="aspect_ratio must be one of: 1:1, 4:3, 3:4, 16:9, 9:16"):
128
+ client.text_to_image.create(model="z-image", prompt="hi there", aspect_ratio="2:1")