agentgen 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,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentgen
3
+ Version: 0.1.0
4
+ Summary: Python client for the AgentGen API — HTML to PDF and Image
5
+ License: MIT
6
+ Keywords: agentgen,html-to-image,html-to-pdf,pdf,screenshot
7
+ Requires-Python: >=3.9
8
+ Requires-Dist: httpx>=0.25.0
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
11
+ Requires-Dist: pytest>=8.0; extra == 'dev'
@@ -0,0 +1,357 @@
1
+ # agentgen · Python SDK
2
+
3
+ Python client for the [AgentGen API](https://www.agent-gen.com) — HTML → PDF and HTML → Image generation.
4
+
5
+ - **Sync + async** — `AgentGenClient` for regular code, `AsyncAgentGenClient` for `asyncio` / `async`/`await`
6
+ - **Typed** — dataclass-based request/response types with full type annotations
7
+ - **Lightweight** — single dependency: [`httpx`](https://www.python-httpx.org/)
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install agentgen
15
+ ```
16
+
17
+ Requires **Python 3.9+** and `httpx >= 0.25`.
18
+
19
+ ---
20
+
21
+ ## Quick start
22
+
23
+ ```python
24
+ from agentgen import AgentGenClient, GenerateImageOptions, PdfPage
25
+
26
+ client = AgentGenClient(api_key="agk_...")
27
+
28
+ # Render HTML to a PNG image
29
+ image = client.generate_image(GenerateImageOptions(
30
+ html="<h1 style='font-family:sans-serif'>Hello, world!</h1>",
31
+ width=1200,
32
+ height=630,
33
+ ))
34
+ print(image.url) # https://…/output.png
35
+
36
+ # Render HTML to a PDF
37
+ pdf = client.generate_pdf(PdfPage(
38
+ html="<h1>Invoice #42</h1><p>Amount due: $99.00</p>",
39
+ format="A4",
40
+ ))
41
+ print(pdf.url) # https://…/output.pdf
42
+
43
+ # Check token balance
44
+ balance = client.get_balance()
45
+ print(f"Remaining tokens: {balance.tokens}")
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Client classes
51
+
52
+ ### `AgentGenClient` (synchronous)
53
+
54
+ ```python
55
+ from agentgen import AgentGenClient
56
+
57
+ client = AgentGenClient(
58
+ api_key="agk_...", # required
59
+ base_url="https://www.agent-gen.com/api", # optional — default shown
60
+ )
61
+ ```
62
+
63
+ ### `AsyncAgentGenClient` (async / await)
64
+
65
+ ```python
66
+ from agentgen import AsyncAgentGenClient
67
+
68
+ client = AsyncAgentGenClient(api_key="agk_...")
69
+
70
+ async def main():
71
+ image = await client.generate_image(...)
72
+ ```
73
+
74
+ Both classes expose exactly the same methods with the same signatures. The only difference is that `AsyncAgentGenClient` returns coroutines that must be awaited.
75
+
76
+ ---
77
+
78
+ ## Methods
79
+
80
+ ### `generate_image(options)` → `GenerateImageResult`
81
+
82
+ Renders an HTML string to a screenshot image. **Costs 1 token.**
83
+
84
+ ```python
85
+ from agentgen import GenerateImageOptions
86
+
87
+ result = client.generate_image(GenerateImageOptions(
88
+ html="<div style='background:#6366f1;color:#fff;padding:40px'>Hello</div>",
89
+ width=1200, # px, default 1200
90
+ height=630, # px, default 630
91
+ format="png", # "png" | "jpeg" | "webp", default "png"
92
+ device_scale_factor=2, # 1–3, default 2 (retina-quality)
93
+ ))
94
+
95
+ print(result.url) # public URL, valid for 24 h
96
+ print(result.width) # actual rendered width
97
+ print(result.height) # actual rendered height
98
+ print(result.format) # "png" | "jpeg" | "webp"
99
+ print(result.tokens_used) # always 1
100
+ print(result.request_id) # unique request identifier
101
+ ```
102
+
103
+ Only `html` is required — all other fields default to `None` (server applies defaults).
104
+
105
+ ---
106
+
107
+ ### `generate_pdf(options)` → `GeneratePdfResult`
108
+
109
+ Renders HTML to a PDF document. **Costs 2 tokens per page.**
110
+
111
+ Pass a single `PdfPage` object for a one-page PDF, or a **list** of `PdfPage` objects for a multi-page PDF.
112
+
113
+ #### Single-page PDF
114
+
115
+ ```python
116
+ from agentgen import PdfPage, PdfMargin
117
+
118
+ result = client.generate_pdf(PdfPage(
119
+ html="""
120
+ <html>
121
+ <body style="font-family:sans-serif;padding:40px">
122
+ <h1>Invoice #42</h1>
123
+ <p>Amount due: $99.00</p>
124
+ </body>
125
+ </html>
126
+ """,
127
+ format="A4", # "A4" | "Letter" | "A3" | "Legal"
128
+ landscape=False, # default False
129
+ print_background=True, # default True
130
+ margin=PdfMargin(
131
+ top="20mm",
132
+ bottom="20mm",
133
+ left="15mm",
134
+ right="15mm",
135
+ ),
136
+ ))
137
+
138
+ print(result.url) # public URL pointing to the PDF
139
+ print(result.pages) # number of pages generated
140
+ print(result.tokens_used) # pages × 2
141
+ print(result.request_id)
142
+ ```
143
+
144
+ #### Multi-page PDF
145
+
146
+ Pass a list of `PdfPage` objects — each entry is an independent page with its own HTML and settings.
147
+
148
+ ```python
149
+ result = client.generate_pdf([
150
+ PdfPage(
151
+ html="<h1 style='padding:40px'>Page 1 — Cover</h1>",
152
+ format="A4",
153
+ ),
154
+ PdfPage(
155
+ html="<h1 style='padding:40px'>Page 2 — Content</h1>",
156
+ format="A4",
157
+ landscape=True,
158
+ ),
159
+ PdfPage(
160
+ html="<h1 style='padding:40px'>Page 3 — Appendix</h1>",
161
+ format="A4",
162
+ margin=PdfMargin(top="10mm", bottom="10mm", left="10mm", right="10mm"),
163
+ ),
164
+ ])
165
+
166
+ print(f"Generated {result.pages} pages, used {result.tokens_used} tokens")
167
+ ```
168
+
169
+ Up to 100 pages per request.
170
+
171
+ ---
172
+
173
+ ### `upload_temp(file, filename=None)` → `UploadTempResult`
174
+
175
+ Uploads a file to temporary storage so you can embed it by URL inside your HTML before calling `generate_image` or `generate_pdf`. **Free — no tokens consumed.** Files are auto-deleted after **24 hours**.
176
+
177
+ ```python
178
+ # Upload from a file path (str or pathlib.Path)
179
+ upload = client.upload_temp("./logo.png")
180
+
181
+ print(upload.url) # public URL, valid for 24 h
182
+ print(upload.key) # storage key
183
+ print(upload.size) # file size in bytes
184
+ print(upload.expires_at) # ISO 8601 expiry timestamp
185
+
186
+ # Now embed the URL in your HTML
187
+ result = client.generate_image(GenerateImageOptions(
188
+ html=f'<img src="{upload.url}" style="width:200px" />',
189
+ width=400,
190
+ height=200,
191
+ ))
192
+ ```
193
+
194
+ ```python
195
+ # Upload from an open file object
196
+ with open("./chart.svg", "rb") as f:
197
+ upload = client.upload_temp(f, filename="chart.svg")
198
+ ```
199
+
200
+ **Accepted types:** `image/png`, `image/jpeg`, `image/webp`, `image/gif`, `image/svg+xml`. Max size: **10 MB**.
201
+
202
+ ---
203
+
204
+ ### `get_balance()` → `BalanceResult`
205
+
206
+ Returns the current token balance.
207
+
208
+ ```python
209
+ balance = client.get_balance()
210
+ print(f"You have {balance.tokens} tokens remaining.")
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Async usage
216
+
217
+ Every method above works identically with `AsyncAgentGenClient`. Just `await` each call:
218
+
219
+ ```python
220
+ import asyncio
221
+ from agentgen import AsyncAgentGenClient, GenerateImageOptions, PdfPage
222
+
223
+ async def main():
224
+ client = AsyncAgentGenClient(api_key="agk_...")
225
+
226
+ # Run both requests concurrently
227
+ image_coro = client.generate_image(GenerateImageOptions(
228
+ html="<h1>OG Image</h1>",
229
+ width=1200,
230
+ height=630,
231
+ ))
232
+ pdf_coro = client.generate_pdf(PdfPage(
233
+ html="<h1>Report</h1>",
234
+ format="A4",
235
+ ))
236
+
237
+ image, pdf = await asyncio.gather(image_coro, pdf_coro)
238
+ print(image.url, pdf.url)
239
+
240
+ asyncio.run(main())
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Error handling
246
+
247
+ ```python
248
+ from agentgen import AgentGenClient, AgentGenError, InsufficientTokensError, GenerateImageOptions
249
+
250
+ client = AgentGenClient(api_key="agk_...")
251
+
252
+ try:
253
+ result = client.generate_image(GenerateImageOptions(html="<h1>Hello</h1>"))
254
+ print(result.url)
255
+
256
+ except InsufficientTokensError as e:
257
+ print(f"Not enough tokens. Have {e.balance}, need {e.required}.")
258
+ print(f"Buy more at: {e.buy_more_url}")
259
+
260
+ except AgentGenError as e:
261
+ print(f"API error {e.status}: {e}")
262
+ if e.detail:
263
+ print(f"Detail: {e.detail}")
264
+ ```
265
+
266
+ ### Exception classes
267
+
268
+ #### `AgentGenError(Exception)`
269
+
270
+ Raised for all API errors (4xx / 5xx) except HTTP 402.
271
+
272
+ | Attribute | Type | Description |
273
+ |-----------|------|-------------|
274
+ | `args[0]` / `str(e)` | `str` | Human-readable error message |
275
+ | `status` | `int` | HTTP status code |
276
+ | `detail` | `str \| None` | Optional additional detail from the API |
277
+ | `details` | `dict \| None` | Validation field errors (present on HTTP 422) |
278
+
279
+ #### `InsufficientTokensError(AgentGenError)`
280
+
281
+ Raised when the account has too few tokens (HTTP 402).
282
+
283
+ | Attribute | Type | Description |
284
+ |-----------|------|-------------|
285
+ | `balance` | `int` | Current token balance |
286
+ | `required` | `int` | Tokens needed for this request |
287
+ | `buy_more_url` | `str` | Direct link to purchase more tokens |
288
+
289
+ ---
290
+
291
+ ## Type reference
292
+
293
+ ```python
294
+ from agentgen import (
295
+ # Clients
296
+ AgentGenClient,
297
+ AsyncAgentGenClient,
298
+
299
+ # Request types
300
+ GenerateImageOptions,
301
+ PdfPage,
302
+ PdfMargin,
303
+
304
+ # Response types
305
+ GenerateImageResult,
306
+ GeneratePdfResult,
307
+ UploadTempResult,
308
+ BalanceResult,
309
+
310
+ # Literal type aliases
311
+ ImageFormat, # Literal["png", "jpeg", "webp"]
312
+ PdfFormat, # Literal["A4", "Letter", "A3", "Legal"]
313
+
314
+ # Exceptions
315
+ AgentGenError,
316
+ InsufficientTokensError,
317
+ )
318
+ ```
319
+
320
+ ### `GenerateImageOptions` fields
321
+
322
+ | Field | Type | Default | Description |
323
+ |-------|------|---------|-------------|
324
+ | `html` | `str` | **required** | HTML to render (max 500 KB) |
325
+ | `width` | `int \| None` | 1200 | Viewport width in px (1–5000) |
326
+ | `height` | `int \| None` | 630 | Viewport height in px (1–5000) |
327
+ | `format` | `ImageFormat \| None` | `"png"` | Output format |
328
+ | `device_scale_factor` | `float \| None` | 2.0 | Device pixel ratio (1–3) |
329
+
330
+ ### `PdfPage` fields
331
+
332
+ | Field | Type | Default | Description |
333
+ |-------|------|---------|-------------|
334
+ | `html` | `str` | **required** | HTML to render (max 500 KB) |
335
+ | `format` | `PdfFormat \| None` | `"A4"` | Paper size |
336
+ | `width` | `str \| None` | — | Custom width, e.g. `"8.5in"` |
337
+ | `height` | `str \| None` | — | Custom height, e.g. `"11in"` |
338
+ | `landscape` | `bool \| None` | `False` | Landscape orientation |
339
+ | `margin` | `PdfMargin \| None` | — | Page margins |
340
+ | `print_background` | `bool \| None` | `True` | Render CSS backgrounds |
341
+
342
+ ### `PdfMargin` fields
343
+
344
+ All fields are `str | None` and accept any CSS length value (e.g. `"20mm"`, `"1in"`, `"72pt"`).
345
+
346
+ ---
347
+
348
+ ## Token pricing
349
+
350
+ | Operation | Cost |
351
+ |-----------|------|
352
+ | Generate image | 1 token |
353
+ | Generate PDF (per page) | 2 tokens |
354
+ | Upload temp file | Free |
355
+ | Check balance | Free |
356
+
357
+ Purchase tokens at [agent-gen.com](https://www.agent-gen.com).
@@ -0,0 +1,52 @@
1
+ """AgentGen Python SDK — HTML to PDF and Image.
2
+
3
+ Quick start::
4
+
5
+ from agentgen import AgentGenClient, GenerateImageOptions
6
+
7
+ client = AgentGenClient(api_key="agk_...")
8
+
9
+ # Generate an image
10
+ img = client.generate_image(GenerateImageOptions(html="<h1>Hello</h1>"))
11
+ print(img.url)
12
+
13
+ # Generate a PDF
14
+ from agentgen import PdfPage
15
+ pdf = client.generate_pdf(PdfPage(html="<h1>Invoice</h1>", format="A4"))
16
+ print(pdf.url)
17
+ """
18
+
19
+ from .client import AgentGenClient, AsyncAgentGenClient
20
+ from .errors import AgentGenError, InsufficientTokensError
21
+ from .types import (
22
+ BalanceResult,
23
+ GenerateImageOptions,
24
+ GenerateImageResult,
25
+ GeneratePdfResult,
26
+ ImageFormat,
27
+ PdfFormat,
28
+ PdfMargin,
29
+ PdfPage,
30
+ UploadTempResult,
31
+ )
32
+
33
+ __all__ = [
34
+ # Clients
35
+ "AgentGenClient",
36
+ "AsyncAgentGenClient",
37
+ # Request types
38
+ "GenerateImageOptions",
39
+ "PdfMargin",
40
+ "PdfPage",
41
+ # Response types
42
+ "GenerateImageResult",
43
+ "GeneratePdfResult",
44
+ "UploadTempResult",
45
+ "BalanceResult",
46
+ # Literals
47
+ "ImageFormat",
48
+ "PdfFormat",
49
+ # Errors
50
+ "AgentGenError",
51
+ "InsufficientTokensError",
52
+ ]
@@ -0,0 +1,244 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import dataclasses
5
+ from pathlib import Path
6
+ from typing import Any, BinaryIO, Optional, Union
7
+
8
+ import httpx
9
+
10
+ from .errors import AgentGenError, InsufficientTokensError
11
+ from .types import (
12
+ BalanceResult,
13
+ GenerateImageOptions,
14
+ GenerateImageResult,
15
+ GeneratePdfResult,
16
+ PdfPage,
17
+ UploadTempResult,
18
+ )
19
+
20
+ DEFAULT_BASE_URL = "https://www.agent-gen.com/api"
21
+
22
+
23
+ def _to_dict(obj: Any) -> Any:
24
+ """Recursively convert a dataclass to a dict, dropping None values."""
25
+ if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
26
+ return {
27
+ f.name: _to_dict(v)
28
+ for f in dataclasses.fields(obj) # type: ignore[arg-type]
29
+ if (v := getattr(obj, f.name)) is not None
30
+ }
31
+ return obj
32
+
33
+
34
+ def _raise_for_error(response: httpx.Response) -> None:
35
+ data: dict[str, Any] = response.json()
36
+ if response.status_code == 402:
37
+ raise InsufficientTokensError(
38
+ data["error"],
39
+ data["balance"],
40
+ data["required"],
41
+ data["buy_more_url"],
42
+ )
43
+ raise AgentGenError(
44
+ data["error"],
45
+ response.status_code,
46
+ data.get("detail"),
47
+ data.get("details"),
48
+ )
49
+
50
+
51
+ class AgentGenClient:
52
+ """Synchronous AgentGen API client.
53
+
54
+ Example::
55
+
56
+ from agentgen import AgentGenClient, GenerateImageOptions
57
+
58
+ client = AgentGenClient(api_key="agk_...")
59
+ result = client.generate_image(GenerateImageOptions(html="<h1>Hello</h1>"))
60
+ print(result.url)
61
+ """
62
+
63
+ def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL) -> None:
64
+ self._base_url = base_url.rstrip("/")
65
+ self._headers = {"X-API-Key": api_key}
66
+
67
+ def generate_image(self, options: GenerateImageOptions) -> GenerateImageResult:
68
+ """Render HTML to an image (PNG/JPEG/WebP). Costs **1 token**."""
69
+ with httpx.Client() as client:
70
+ r = client.post(
71
+ f"{self._base_url}/v1/generate/image",
72
+ json=_to_dict(options),
73
+ headers=self._headers,
74
+ )
75
+ if not r.is_success:
76
+ _raise_for_error(r)
77
+ return GenerateImageResult(**r.json())
78
+
79
+ def generate_pdf(
80
+ self, options: Union[PdfPage, list[PdfPage]]
81
+ ) -> GeneratePdfResult:
82
+ """Render HTML to a PDF. Costs **2 tokens per page**.
83
+
84
+ Pass a single :class:`PdfPage` for a one-page document, or a list of
85
+ :class:`PdfPage` objects for a multi-page document.
86
+ """
87
+ body = (
88
+ {"pages": [_to_dict(p) for p in options]}
89
+ if isinstance(options, list)
90
+ else _to_dict(options)
91
+ )
92
+ with httpx.Client() as client:
93
+ r = client.post(
94
+ f"{self._base_url}/v1/generate/pdf",
95
+ json=body,
96
+ headers=self._headers,
97
+ )
98
+ if not r.is_success:
99
+ _raise_for_error(r)
100
+ return GeneratePdfResult(**r.json())
101
+
102
+ def upload_temp(
103
+ self,
104
+ file: Union[str, Path, BinaryIO],
105
+ filename: Optional[str] = None,
106
+ ) -> UploadTempResult:
107
+ """Upload a file for use inside HTML templates.
108
+
109
+ **Free** (no tokens). Auto-deleted after **24 hours**.
110
+
111
+ Args:
112
+ file: A file path (str/Path) or an open binary file object.
113
+ filename: Optional filename hint for the upload.
114
+ """
115
+ if isinstance(file, (str, Path)):
116
+ path = Path(file)
117
+ filename = filename or path.name
118
+ file_obj: BinaryIO = open(path, "rb")
119
+ should_close = True
120
+ else:
121
+ file_obj = file
122
+ should_close = False
123
+
124
+ try:
125
+ with httpx.Client() as client:
126
+ r = client.post(
127
+ f"{self._base_url}/v1/upload/temp",
128
+ files={"file": (filename or "upload", file_obj)},
129
+ headers=self._headers,
130
+ )
131
+ finally:
132
+ if should_close:
133
+ file_obj.close() # type: ignore[union-attr]
134
+
135
+ if not r.is_success:
136
+ _raise_for_error(r)
137
+ return UploadTempResult(**r.json())
138
+
139
+ def get_balance(self) -> BalanceResult:
140
+ """Return the current token balance for the API key owner."""
141
+ with httpx.Client() as client:
142
+ r = client.get(
143
+ f"{self._base_url}/v1/balance",
144
+ headers=self._headers,
145
+ )
146
+ if not r.is_success:
147
+ _raise_for_error(r)
148
+ return BalanceResult(**r.json())
149
+
150
+
151
+ class AsyncAgentGenClient:
152
+ """Async AgentGen API client (use with ``async``/``await``).
153
+
154
+ Example::
155
+
156
+ from agentgen import AsyncAgentGenClient, GenerateImageOptions
157
+
158
+ async def main():
159
+ client = AsyncAgentGenClient(api_key="agk_...")
160
+ result = await client.generate_image(GenerateImageOptions(html="<h1>Hello</h1>"))
161
+ print(result.url)
162
+ """
163
+
164
+ def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL) -> None:
165
+ self._base_url = base_url.rstrip("/")
166
+ self._headers = {"X-API-Key": api_key}
167
+
168
+ async def generate_image(
169
+ self, options: GenerateImageOptions
170
+ ) -> GenerateImageResult:
171
+ """Render HTML to an image (PNG/JPEG/WebP). Costs **1 token**."""
172
+ async with httpx.AsyncClient() as client:
173
+ r = await client.post(
174
+ f"{self._base_url}/v1/generate/image",
175
+ json=_to_dict(options),
176
+ headers=self._headers,
177
+ )
178
+ if not r.is_success:
179
+ _raise_for_error(r)
180
+ return GenerateImageResult(**r.json())
181
+
182
+ async def generate_pdf(
183
+ self, options: Union[PdfPage, list[PdfPage]]
184
+ ) -> GeneratePdfResult:
185
+ """Render HTML to a PDF. Costs **2 tokens per page**.
186
+
187
+ Pass a single :class:`PdfPage` or a list for multi-page output.
188
+ """
189
+ body = (
190
+ {"pages": [_to_dict(p) for p in options]}
191
+ if isinstance(options, list)
192
+ else _to_dict(options)
193
+ )
194
+ async with httpx.AsyncClient() as client:
195
+ r = await client.post(
196
+ f"{self._base_url}/v1/generate/pdf",
197
+ json=body,
198
+ headers=self._headers,
199
+ )
200
+ if not r.is_success:
201
+ _raise_for_error(r)
202
+ return GeneratePdfResult(**r.json())
203
+
204
+ async def upload_temp(
205
+ self,
206
+ file: Union[str, Path, bytes],
207
+ filename: Optional[str] = None,
208
+ ) -> UploadTempResult:
209
+ """Upload a file for use inside HTML templates.
210
+
211
+ **Free** (no tokens). Auto-deleted after **24 hours**.
212
+
213
+ Args:
214
+ file: A file path (str/Path) or raw bytes.
215
+ filename: Optional filename hint for the upload.
216
+ """
217
+ if isinstance(file, (str, Path)):
218
+ path = Path(file)
219
+ filename = filename or path.name
220
+ loop = asyncio.get_event_loop()
221
+ content: bytes = await loop.run_in_executor(None, path.read_bytes)
222
+ else:
223
+ content = file
224
+
225
+ async with httpx.AsyncClient() as client:
226
+ r = await client.post(
227
+ f"{self._base_url}/v1/upload/temp",
228
+ files={"file": (filename or "upload", content)},
229
+ headers=self._headers,
230
+ )
231
+ if not r.is_success:
232
+ _raise_for_error(r)
233
+ return UploadTempResult(**r.json())
234
+
235
+ async def get_balance(self) -> BalanceResult:
236
+ """Return the current token balance for the API key owner."""
237
+ async with httpx.AsyncClient() as client:
238
+ r = await client.get(
239
+ f"{self._base_url}/v1/balance",
240
+ headers=self._headers,
241
+ )
242
+ if not r.is_success:
243
+ _raise_for_error(r)
244
+ return BalanceResult(**r.json())
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ class AgentGenError(Exception):
7
+ """Raised when the AgentGen API returns an error response."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ status: int,
13
+ detail: Optional[str] = None,
14
+ details: Optional[dict[str, Any]] = None,
15
+ ) -> None:
16
+ super().__init__(message)
17
+ self.status = status
18
+ self.detail = detail
19
+ self.details = details
20
+
21
+ def __repr__(self) -> str:
22
+ return f"{self.__class__.__name__}(status={self.status}, message={str(self)!r})"
23
+
24
+
25
+ class InsufficientTokensError(AgentGenError):
26
+ """Raised when the account has too few tokens for the requested operation."""
27
+
28
+ def __init__(
29
+ self,
30
+ message: str,
31
+ balance: int,
32
+ required: int,
33
+ buy_more_url: str,
34
+ ) -> None:
35
+ super().__init__(message, 402)
36
+ self.balance = balance
37
+ self.required = required
38
+ self.buy_more_url = buy_more_url
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ from typing import Literal, Optional
5
+
6
+ ImageFormat = Literal["png", "jpeg", "webp"]
7
+ PdfFormat = Literal["A4", "Letter", "A3", "Legal"]
8
+
9
+
10
+ @dataclasses.dataclass
11
+ class GenerateImageOptions:
12
+ """Options for HTML → image generation."""
13
+
14
+ html: str
15
+ """HTML content to render (max 500 KB)."""
16
+ width: Optional[int] = None
17
+ """Viewport width in px (1–5000, default 1200)."""
18
+ height: Optional[int] = None
19
+ """Viewport height in px (1–5000, default 630)."""
20
+ format: Optional[ImageFormat] = None
21
+ """Output format: "png" | "jpeg" | "webp" (default "png")."""
22
+ device_scale_factor: Optional[float] = None
23
+ """Device pixel ratio (1–3, default 2)."""
24
+
25
+
26
+ @dataclasses.dataclass
27
+ class GenerateImageResult:
28
+ url: str
29
+ width: int
30
+ height: int
31
+ format: ImageFormat
32
+ tokens_used: int
33
+ request_id: str
34
+
35
+
36
+ @dataclasses.dataclass
37
+ class PdfMargin:
38
+ top: Optional[str] = None
39
+ bottom: Optional[str] = None
40
+ left: Optional[str] = None
41
+ right: Optional[str] = None
42
+
43
+
44
+ @dataclasses.dataclass
45
+ class PdfPage:
46
+ """A single PDF page."""
47
+
48
+ html: str
49
+ """HTML content to render (max 500 KB)."""
50
+ format: Optional[PdfFormat] = None
51
+ """Paper size: "A4" | "Letter" | "A3" | "Legal" (default "A4")."""
52
+ width: Optional[str] = None
53
+ """Custom page width, e.g. "8.5in" (overrides format)."""
54
+ height: Optional[str] = None
55
+ """Custom page height, e.g. "11in"."""
56
+ landscape: Optional[bool] = None
57
+ """Landscape orientation (default False)."""
58
+ margin: Optional[PdfMargin] = None
59
+ """Page margins."""
60
+ print_background: Optional[bool] = None
61
+ """Print CSS backgrounds (default True)."""
62
+
63
+
64
+ @dataclasses.dataclass
65
+ class GeneratePdfResult:
66
+ url: str
67
+ pages: int
68
+ tokens_used: int
69
+ request_id: str
70
+
71
+
72
+ @dataclasses.dataclass
73
+ class UploadTempResult:
74
+ url: str
75
+ key: str
76
+ size: int
77
+ expires_in: int
78
+ expires_at: str
79
+
80
+
81
+ @dataclasses.dataclass
82
+ class BalanceResult:
83
+ tokens: int
@@ -0,0 +1,20 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "agentgen"
7
+ version = "0.1.0"
8
+ description = "Python client for the AgentGen API — HTML to PDF and Image"
9
+ requires-python = ">=3.9"
10
+ license = { text = "MIT" }
11
+ keywords = ["agentgen", "html-to-pdf", "html-to-image", "pdf", "screenshot"]
12
+ dependencies = [
13
+ "httpx>=0.25.0",
14
+ ]
15
+
16
+ [project.optional-dependencies]
17
+ dev = ["pytest>=8.0", "pytest-asyncio>=0.24"]
18
+
19
+ [tool.hatch.build.targets.wheel]
20
+ packages = ["agentgen"]