tena 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.
tena-0.1.0/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ output/*
7
+ data/*
8
+ wheels/
9
+ *.egg-info
10
+
11
+ # Virtual environments
12
+ .venv
13
+
14
+ .DS_Store
15
+
16
+ *.swp
17
+ *.swo
18
+ .env
@@ -0,0 +1 @@
1
+ 3.12
tena-0.1.0/AGENTS.md ADDED
@@ -0,0 +1,18 @@
1
+ # AGENTS.md
2
+
3
+ 始终使用英文来思考,使用中文来回复用户和完成计划
4
+
5
+ 关于项目的详细描述 ./README.md
6
+
7
+ ## Prerequisites
8
+
9
+ - Python 3.10 or higher
10
+ - uv package manager
11
+
12
+ ## Development Guidelines
13
+
14
+ - 不要 git 提交代码,除非用户明确要求
15
+ - 对于复杂的逻辑使用清晰的注释来描述
16
+ - 涉及到项目结构调整,要同步更新 README.md 中的相关内容
17
+ - 除非明确要求,你不用写测试代码。除了编译检查/lint,你不用运行代码进行测试,可以提醒我如何手动运行。
18
+ - 尽可能使用明确的类型标注
tena-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: tena
3
+ Version: 0.1.0
4
+ Summary: My Text-to-Image kits
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: google-genai>=2.10.0
7
+ Requires-Dist: httpx>=0.28.1
8
+ Requires-Dist: openai>=2.44.0
9
+ Requires-Dist: pytest>=9.0.2
10
+ Requires-Dist: python-dotenv>=1.2.1
11
+ Description-Content-Type: text/markdown
12
+
13
+ # tena
14
+
15
+ `tena` provides a small async wrapper for text-to-image model calls.
16
+
17
+ ## Install
18
+
19
+ Prerequisites:
20
+
21
+ - Python 3.10+
22
+ - uv
23
+
24
+ Dependencies are declared in `pyproject.toml`:
25
+
26
+ - `openai`
27
+ - `google-genai`
28
+ - `httpx`
29
+ - `pytest`
30
+ - `python-dotenv`
31
+
32
+ ## Image generation
33
+
34
+ ### Python API
35
+
36
+ Use `draw` with a registered `model_path`. The function returns a list of
37
+ `GeneratedImage` objects. Each object contains image bytes and a MIME type.
38
+
39
+ ```python
40
+ import asyncio
41
+ from pathlib import Path
42
+
43
+ from tena import draw, suffix_for_mime_type
44
+
45
+
46
+ async def main() -> None:
47
+ images = await draw(
48
+ model_path="openrouter/gpt-image-2",
49
+ prompt="A clean product photo of a ceramic cup",
50
+ size="1024x1024",
51
+ number=1,
52
+ input_images=[
53
+ "./reference-1.png",
54
+ "https://example.com/reference-2.png",
55
+ ],
56
+ )
57
+
58
+ image = images[0]
59
+ suffix = suffix_for_mime_type(image.mime_type)
60
+ Path(f"result{suffix}").write_bytes(image.data)
61
+
62
+
63
+ asyncio.run(main())
64
+ ```
65
+
66
+ The package exports the reusable API from `tena/__init__.py`. `tena/main.py` is
67
+ only the CLI entrypoint; core image generation logic lives in `tena/image.py`.
68
+
69
+ ### CLI
70
+
71
+ The `tena` command is a thin wrapper around the Python API.
72
+
73
+ Generate one image and write it to a required output path:
74
+
75
+ ```bash
76
+ uv run tena --model-path openrouter/gpt-image-2 --prompt "A clean product photo of a ceramic cup" --output ./result.png --size 1024x1024
77
+ ```
78
+
79
+ The output path supports `~`, including `--output ~/Downloads/result.png` and
80
+ `--output=~/Downloads/result.png`.
81
+
82
+ Use `-` as the prompt to read from stdin:
83
+
84
+ ```bash
85
+ echo "A simple red cube on a clean white background" | uv run tena --model-path openrouter/gpt-image-2 --prompt - --output ./result.png --size 1024x1024
86
+ ```
87
+
88
+ Generate multiple images with `--number`. The first image uses the exact output
89
+ path, and later images are numbered:
90
+
91
+ ```bash
92
+ uv run tena --model-path zenmux/gpt-image-2 --prompt "A blue sphere" --output ./result.png --number 2 --size 1024x1024
93
+ ```
94
+
95
+ This writes `result.png` and `result-2.png`.
96
+
97
+ Provide reference images with repeated `--input-image` arguments. Each value can
98
+ be a local file path or an `http`/`https` URL:
99
+
100
+ ```bash
101
+ uv run tena \
102
+ --model-path openrouter/gpt-image-2 \
103
+ --prompt "Create a studio product photo using the reference objects" \
104
+ --input-image ./reference-1.png \
105
+ --input-image https://example.com/reference-2.png \
106
+ --output ./result.png \
107
+ --size 1024x1024
108
+ ```
109
+
110
+ URL images are downloaded to `/tmp/tena/input_images` and cached by URL, so the
111
+ same URL is reused on later runs instead of downloaded again.
112
+
113
+ CLI logs are written to stderr and default to `INFO`. The generated output paths
114
+ are still written to stdout. Use `--log-level WARNING` to hide normal progress
115
+ logs, or `--log-level DEBUG` for diagnostic details:
116
+
117
+ ```bash
118
+ uv run tena --model-path openrouter/gpt-image-2 --prompt "A blue sphere" --output ./result.png --log-level WARNING
119
+ ```
120
+
121
+ INFO logs include concise request metadata, URL cache/download status, byte
122
+ counts, and elapsed times. DEBUG logs include more detailed SDK and input image
123
+ diagnostics. Logs do not include API keys, full prompts, full input URLs, or
124
+ image data.
125
+
126
+ ### Return object
127
+
128
+ ```python
129
+ @dataclass(frozen=True)
130
+ class GeneratedImage:
131
+ data: bytes
132
+ mime_type: str
133
+ ```
134
+
135
+ ### Registered models
136
+
137
+ Models are currently registered in code. `model_path` is the lookup key, and
138
+ `model_realname` is the model id sent to the upstream API.
139
+
140
+ ```python
141
+ @dataclass(frozen=True)
142
+ class ImageModel:
143
+ model_displayname: str
144
+ model_realname: str
145
+ client: Literal[
146
+ "openai",
147
+ "openrouter",
148
+ "gemini-interactions",
149
+ "gemini-generate-content",
150
+ ]
151
+ api_key_env: str
152
+ base_url: str | None = None
153
+ default_mime_type: str = "image/png"
154
+ ```
155
+
156
+ Current model paths:
157
+
158
+ - `302ai/gpt-image-2`
159
+ - `302ai/gemini-3.1-flash-image-preview`
160
+ - `google/gemini-3.1-flash-image`
161
+ - `openrouter/gpt-image-2`
162
+ - `zenmux/gpt-image-2`
163
+ - `zenmux/gemini-3.1-flash-image`
164
+
165
+ `client` means which upstream API contract is used. OpenAI-compatible gateways
166
+ can use `client="openai"` with a custom `base_url`. OpenRouter uses
167
+ `client="openrouter"` and calls its `/images` unified image generation endpoint
168
+ directly.
169
+
170
+ Gemini image models use two different API contracts:
171
+
172
+ - `gemini-interactions` uses Google's Interactions API and is used for direct
173
+ Google Gemini access.
174
+ - `gemini-generate-content` uses the Generate Content API and is used for
175
+ Gemini-compatible gateways such as Zenmux and 302AI.
176
+
177
+ ### Environment variables
178
+
179
+ Set the API key required by the selected model entry. Values can be provided by
180
+ the process environment or by a project-root `.env` file:
181
+
182
+ - `OPENROUTER_API_KEY`
183
+ - `ZENMUX_API_KEY`
184
+ - `AI302_API_KEY`
185
+ - `GEMINI_API_KEY`
186
+
187
+ ### Gemini size format
188
+
189
+ Gemini image models use a single `size` string that is parsed into
190
+ the Gemini image response configuration. Interactions requests use
191
+ `response_format`; Generate Content requests use `image_config`.
192
+
193
+ - `16:9` -> `aspect_ratio`
194
+ - `2K` or `4K` -> `image_size`
195
+ - `16:9@2K` -> both `aspect_ratio` and `image_size`
196
+ - `auto` -> no explicit Gemini `response_format`
197
+
198
+ OpenAI-compatible and OpenRouter clients pass `size` through as the API `size`
199
+ parameter.
200
+
201
+ ## Not implemented yet
202
+
203
+ The following parameters are part of the public function signature but are not
204
+ implemented yet:
205
+
206
+ - `web_search`
207
+
208
+ Passing `web_search` raises `NotImplementedError`.
209
+
210
+ ## Integration tests
211
+
212
+ Live image generation tests are grouped under `tests/integration`. Set the API
213
+ key for the model you want to test in the environment or in the project `.env`
214
+ file:
215
+
216
+ - `OPENROUTER_API_KEY` for `openrouter/gpt-image-2`
217
+ - `ZENMUX_API_KEY` for `zenmux/gpt-image-2`
218
+
219
+ Then run:
220
+
221
+ ```bash
222
+ pytest tests/integration
223
+ ```
224
+
225
+ Tests with missing API keys are skipped. When a test succeeds, it writes the
226
+ generated image to `~/Downloads` with a `tena-<provider>-gpt-image-2` filename
227
+ prefix.
tena-0.1.0/README.md ADDED
@@ -0,0 +1,215 @@
1
+ # tena
2
+
3
+ `tena` provides a small async wrapper for text-to-image model calls.
4
+
5
+ ## Install
6
+
7
+ Prerequisites:
8
+
9
+ - Python 3.10+
10
+ - uv
11
+
12
+ Dependencies are declared in `pyproject.toml`:
13
+
14
+ - `openai`
15
+ - `google-genai`
16
+ - `httpx`
17
+ - `pytest`
18
+ - `python-dotenv`
19
+
20
+ ## Image generation
21
+
22
+ ### Python API
23
+
24
+ Use `draw` with a registered `model_path`. The function returns a list of
25
+ `GeneratedImage` objects. Each object contains image bytes and a MIME type.
26
+
27
+ ```python
28
+ import asyncio
29
+ from pathlib import Path
30
+
31
+ from tena import draw, suffix_for_mime_type
32
+
33
+
34
+ async def main() -> None:
35
+ images = await draw(
36
+ model_path="openrouter/gpt-image-2",
37
+ prompt="A clean product photo of a ceramic cup",
38
+ size="1024x1024",
39
+ number=1,
40
+ input_images=[
41
+ "./reference-1.png",
42
+ "https://example.com/reference-2.png",
43
+ ],
44
+ )
45
+
46
+ image = images[0]
47
+ suffix = suffix_for_mime_type(image.mime_type)
48
+ Path(f"result{suffix}").write_bytes(image.data)
49
+
50
+
51
+ asyncio.run(main())
52
+ ```
53
+
54
+ The package exports the reusable API from `tena/__init__.py`. `tena/main.py` is
55
+ only the CLI entrypoint; core image generation logic lives in `tena/image.py`.
56
+
57
+ ### CLI
58
+
59
+ The `tena` command is a thin wrapper around the Python API.
60
+
61
+ Generate one image and write it to a required output path:
62
+
63
+ ```bash
64
+ uv run tena --model-path openrouter/gpt-image-2 --prompt "A clean product photo of a ceramic cup" --output ./result.png --size 1024x1024
65
+ ```
66
+
67
+ The output path supports `~`, including `--output ~/Downloads/result.png` and
68
+ `--output=~/Downloads/result.png`.
69
+
70
+ Use `-` as the prompt to read from stdin:
71
+
72
+ ```bash
73
+ echo "A simple red cube on a clean white background" | uv run tena --model-path openrouter/gpt-image-2 --prompt - --output ./result.png --size 1024x1024
74
+ ```
75
+
76
+ Generate multiple images with `--number`. The first image uses the exact output
77
+ path, and later images are numbered:
78
+
79
+ ```bash
80
+ uv run tena --model-path zenmux/gpt-image-2 --prompt "A blue sphere" --output ./result.png --number 2 --size 1024x1024
81
+ ```
82
+
83
+ This writes `result.png` and `result-2.png`.
84
+
85
+ Provide reference images with repeated `--input-image` arguments. Each value can
86
+ be a local file path or an `http`/`https` URL:
87
+
88
+ ```bash
89
+ uv run tena \
90
+ --model-path openrouter/gpt-image-2 \
91
+ --prompt "Create a studio product photo using the reference objects" \
92
+ --input-image ./reference-1.png \
93
+ --input-image https://example.com/reference-2.png \
94
+ --output ./result.png \
95
+ --size 1024x1024
96
+ ```
97
+
98
+ URL images are downloaded to `/tmp/tena/input_images` and cached by URL, so the
99
+ same URL is reused on later runs instead of downloaded again.
100
+
101
+ CLI logs are written to stderr and default to `INFO`. The generated output paths
102
+ are still written to stdout. Use `--log-level WARNING` to hide normal progress
103
+ logs, or `--log-level DEBUG` for diagnostic details:
104
+
105
+ ```bash
106
+ uv run tena --model-path openrouter/gpt-image-2 --prompt "A blue sphere" --output ./result.png --log-level WARNING
107
+ ```
108
+
109
+ INFO logs include concise request metadata, URL cache/download status, byte
110
+ counts, and elapsed times. DEBUG logs include more detailed SDK and input image
111
+ diagnostics. Logs do not include API keys, full prompts, full input URLs, or
112
+ image data.
113
+
114
+ ### Return object
115
+
116
+ ```python
117
+ @dataclass(frozen=True)
118
+ class GeneratedImage:
119
+ data: bytes
120
+ mime_type: str
121
+ ```
122
+
123
+ ### Registered models
124
+
125
+ Models are currently registered in code. `model_path` is the lookup key, and
126
+ `model_realname` is the model id sent to the upstream API.
127
+
128
+ ```python
129
+ @dataclass(frozen=True)
130
+ class ImageModel:
131
+ model_displayname: str
132
+ model_realname: str
133
+ client: Literal[
134
+ "openai",
135
+ "openrouter",
136
+ "gemini-interactions",
137
+ "gemini-generate-content",
138
+ ]
139
+ api_key_env: str
140
+ base_url: str | None = None
141
+ default_mime_type: str = "image/png"
142
+ ```
143
+
144
+ Current model paths:
145
+
146
+ - `302ai/gpt-image-2`
147
+ - `302ai/gemini-3.1-flash-image-preview`
148
+ - `google/gemini-3.1-flash-image`
149
+ - `openrouter/gpt-image-2`
150
+ - `zenmux/gpt-image-2`
151
+ - `zenmux/gemini-3.1-flash-image`
152
+
153
+ `client` means which upstream API contract is used. OpenAI-compatible gateways
154
+ can use `client="openai"` with a custom `base_url`. OpenRouter uses
155
+ `client="openrouter"` and calls its `/images` unified image generation endpoint
156
+ directly.
157
+
158
+ Gemini image models use two different API contracts:
159
+
160
+ - `gemini-interactions` uses Google's Interactions API and is used for direct
161
+ Google Gemini access.
162
+ - `gemini-generate-content` uses the Generate Content API and is used for
163
+ Gemini-compatible gateways such as Zenmux and 302AI.
164
+
165
+ ### Environment variables
166
+
167
+ Set the API key required by the selected model entry. Values can be provided by
168
+ the process environment or by a project-root `.env` file:
169
+
170
+ - `OPENROUTER_API_KEY`
171
+ - `ZENMUX_API_KEY`
172
+ - `AI302_API_KEY`
173
+ - `GEMINI_API_KEY`
174
+
175
+ ### Gemini size format
176
+
177
+ Gemini image models use a single `size` string that is parsed into
178
+ the Gemini image response configuration. Interactions requests use
179
+ `response_format`; Generate Content requests use `image_config`.
180
+
181
+ - `16:9` -> `aspect_ratio`
182
+ - `2K` or `4K` -> `image_size`
183
+ - `16:9@2K` -> both `aspect_ratio` and `image_size`
184
+ - `auto` -> no explicit Gemini `response_format`
185
+
186
+ OpenAI-compatible and OpenRouter clients pass `size` through as the API `size`
187
+ parameter.
188
+
189
+ ## Not implemented yet
190
+
191
+ The following parameters are part of the public function signature but are not
192
+ implemented yet:
193
+
194
+ - `web_search`
195
+
196
+ Passing `web_search` raises `NotImplementedError`.
197
+
198
+ ## Integration tests
199
+
200
+ Live image generation tests are grouped under `tests/integration`. Set the API
201
+ key for the model you want to test in the environment or in the project `.env`
202
+ file:
203
+
204
+ - `OPENROUTER_API_KEY` for `openrouter/gpt-image-2`
205
+ - `ZENMUX_API_KEY` for `zenmux/gpt-image-2`
206
+
207
+ Then run:
208
+
209
+ ```bash
210
+ pytest tests/integration
211
+ ```
212
+
213
+ Tests with missing API keys are skipped. When a test succeeds, it writes the
214
+ generated image to `~/Downloads` with a `tena-<provider>-gpt-image-2` filename
215
+ prefix.
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "tena"
3
+ version = "0.1.0"
4
+ description = "My Text-to-Image kits"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "google-genai>=2.10.0",
9
+ "httpx>=0.28.1",
10
+ "openai>=2.44.0",
11
+ "pytest>=9.0.2",
12
+ "python-dotenv>=1.2.1",
13
+ ]
14
+
15
+ [project.scripts]
16
+ tena = "tena.main:main"
17
+
18
+ [build-system]
19
+ requires = ["hatchling"]
20
+ build-backend = "hatchling.build"
21
+
22
+ [tool.pytest.ini_options]
23
+ pythonpath = ["."]
24
+ testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ from tena.image import GeneratedImage, ImageModel, draw, suffix_for_mime_type
2
+
3
+ __all__ = ["GeneratedImage", "ImageModel", "draw", "suffix_for_mime_type"]