galley-render 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,19 @@
1
+ .env
2
+ .env.*
3
+ !.env.example
4
+ *.swp
5
+ notes
6
+ node_modules/
7
+ dist/
8
+ .turbo/
9
+ coverage/
10
+ .dev-storage/
11
+ test-output/
12
+ *.tsbuildinfo
13
+ __pycache__/
14
+ .venv/
15
+ *.egg-info/
16
+ *.pyc
17
+
18
+ # MCP registry Ed25519 private key material (never commit)
19
+ ops/listings/.secrets/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Galley Render
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.
@@ -0,0 +1,252 @@
1
+ Metadata-Version: 2.5
2
+ Name: galley-render
3
+ Version: 0.1.0
4
+ Summary: Galley Render — JSON in, PDF out. Official Python client for the document API agents can sign themselves up for.
5
+ Project-URL: Homepage, https://galleyrender.com
6
+ Project-URL: Documentation, https://galleyrender.com/docs/sdks/python
7
+ Project-URL: Source, https://github.com/mattmueller/galley
8
+ Project-URL: Changelog, https://galleyrender.com/docs/sdks/python/changelog
9
+ Project-URL: Issues, https://galleyrender.com/support
10
+ Author-email: Galley Render <hello@galleyrender.com>
11
+ Maintainer-email: Galley Render <support@galleyrender.com>
12
+ License-Expression: MIT
13
+ License-File: LICENSE
14
+ Keywords: agents,certificate,document-api,html-to-pdf,invoice,mcp,og-image,pdf,pdf-generation,png
15
+ Classifier: Development Status :: 4 - Beta
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
25
+ Classifier: Topic :: Printing
26
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.9
29
+ Requires-Dist: httpx>=0.27
30
+ Provides-Extra: dev
31
+ Requires-Dist: build>=1.2; extra == 'dev'
32
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
33
+ Requires-Dist: pytest>=8; extra == 'dev'
34
+ Requires-Dist: twine>=5; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # galley-render
38
+
39
+ **JSON in, PDF out.** The official Python client for [Galley Render](https://galleyrender.com) — a
40
+ document API for agents and the programs they write. A template plus a JSON payload becomes a PDF,
41
+ PNG or JPG behind a signed URL, deterministically and cached, so the same input always returns the
42
+ same file and an identical repeat call is free.
43
+
44
+ - Sync and async clients with the same surface.
45
+ - One dependency: `httpx`.
46
+ - Typed responses from the API's own [OpenAPI spec](https://api.galleyrender.com/openapi.json),
47
+ with `.raw` kept intact and `py.typed` shipped.
48
+ - Retries 429 and 5xx with exponential backoff and full jitter.
49
+ - Downloads signed URLs, and re-signs them when they expire.
50
+ - Starts a **50-render keyless trial** with no signup and no card.
51
+
52
+ ```bash
53
+ pip install galley-render
54
+ ```
55
+
56
+ Python 3.9 or newer.
57
+
58
+ ## Quickstart
59
+
60
+ ```python
61
+ import os
62
+ from galley_render import Galley
63
+
64
+ galley = Galley(api_key=os.environ["GALLEY_API_KEY"])
65
+
66
+ render = galley.render(
67
+ "invoice@1",
68
+ format="pdf",
69
+ data={
70
+ "invoice_number": "INV-1042",
71
+ "customer": {"name": "Acme Corp"},
72
+ "line_items": [{"description": "Consulting", "quantity": 12, "unit_price": 150}],
73
+ },
74
+ )
75
+
76
+ print(render.url) # signed, short-lived
77
+ galley.download(render, to_file="invoice.pdf")
78
+ ```
79
+
80
+ Async is the same thing with `await`:
81
+
82
+ ```python
83
+ from galley_render import AsyncGalley
84
+
85
+ async with AsyncGalley() as galley: # reads GALLEY_API_KEY
86
+ render = await galley.render("invoice@1", format="pdf", data=payload)
87
+ pdf = await galley.download(render)
88
+ ```
89
+
90
+ ### No key yet
91
+
92
+ `start_trial()` mints a real 50-render account through Galley's MCP server and hands back a key that
93
+ works everywhere in this package and against the REST API.
94
+
95
+ ```python
96
+ from galley_render import Galley, start_trial
97
+
98
+ trial = start_trial(client_id="my-app") # reuse client_id to keep the same trial
99
+ print(trial.renders_remaining) # 50
100
+
101
+ galley = Galley(api_key=trial.api_key)
102
+ render = galley.render("og-card", format="png", data={"title": "Hello"})
103
+ ```
104
+
105
+ `async_start_trial()` is the awaitable form. To lift the limit, call the `create_account` tool on
106
+ [the MCP server](https://mcp.galleyrender.com/mcp) with an email, or sign up at
107
+ [galleyrender.com](https://galleyrender.com). The trial upgrades in place — nothing it made is lost.
108
+
109
+ ## Configuration
110
+
111
+ ```python
112
+ Galley(
113
+ api_key=None, # default: $GALLEY_API_KEY
114
+ base_url=None, # default: $GALLEY_BASE_URL, then the public API
115
+ timeout=60.0, # seconds, per attempt
116
+ max_retries=3, # extra attempts on 429/5xx and connection failures
117
+ headers=None, # merged into every request
118
+ http_client=None, # bring your own httpx.Client
119
+ )
120
+ ```
121
+
122
+ Both clients are context managers, and close the `httpx` client they created:
123
+
124
+ ```python
125
+ with Galley() as galley:
126
+ ...
127
+ ```
128
+
129
+ ## Rendering
130
+
131
+ ```python
132
+ # Small jobs finish inside the call.
133
+ render = galley.render(
134
+ "certificate@2", # pin the version in anything you ship
135
+ format="pdf", # pdf | png | jpg
136
+ data={"recipient": "Dana Lee", "course": "Rope Access L1"},
137
+ options={"page_size": "Letter", "margin": "18mm", "landscape": True},
138
+ )
139
+
140
+ # A webhook, async_=True or a large payload queues the job instead.
141
+ queued = galley.render("report", data=data, async_=True)
142
+ done = galley.renders.wait(queued.id) # polls with backoff
143
+
144
+ # Or do both in one call, whichever path the API takes.
145
+ finished = galley.render_and_wait("report", data=data)
146
+
147
+ # Up to 50 at a time. A bad item fails alone; the rest still run.
148
+ batch = galley.renders.batch(
149
+ [{"template": "statement@4", "data": c} for c in customers],
150
+ webhook_url="https://example.com/hooks/galley",
151
+ )
152
+ batch.succeeded # the Render objects
153
+ batch.failed # the error envelopes, each with its index
154
+ ```
155
+
156
+ `render.cached` is `True` when the deterministic cache answered: the same template version, data,
157
+ options and format were rendered before, and this call cost nothing.
158
+
159
+ ## Downloading
160
+
161
+ Signed URLs are short-lived; the stored file is not. `download()` takes a render, a render id or a
162
+ URL, and quietly re-signs an expired one.
163
+
164
+ ```python
165
+ data = galley.download(render) # bytes
166
+ galley.download(render, to_file="out/invoice.pdf")
167
+ galley.download("rnd_7hq2m4x8k1bv", to_file="a.png")
168
+ ```
169
+
170
+ ## Templates
171
+
172
+ Templates are code: one self-contained HTML document with inline CSS and Liquid expressions, plus a
173
+ JSON Schema that is the contract for `data`. Versions are immutable and content-addressed.
174
+
175
+ ```python
176
+ galley.templates.list()
177
+ invoice = galley.templates.get("invoice@3")
178
+ invoice.schema # JSON Schema for `data`
179
+ invoice.example # a payload that renders
180
+
181
+ galley.templates.create(
182
+ "welcome-card",
183
+ engine="satori", # fast PNG path for simple flexbox cards
184
+ source="<div style='display:flex'>{{ name }}</div>",
185
+ schema={"type": "object", "required": ["name"], "properties": {"name": {"type": "string"}}},
186
+ example={"name": "Dana"},
187
+ )
188
+
189
+ galley.templates.publish("welcome-card", source="…", message="tighter kerning")
190
+ galley.templates.versions("welcome-card")
191
+
192
+ # Free, renders nothing, and returns the same field errors a render would.
193
+ check = galley.templates.validate("welcome-card", {"name": 42})
194
+ if not check:
195
+ for field in check.errors:
196
+ print(field) # name: must be string (expected string, got number)
197
+ ```
198
+
199
+ ## Usage
200
+
201
+ ```python
202
+ usage = galley.usage()
203
+ usage.billable_units # 1 per PNG/JPG, 1 per PDF page; cache hits are free
204
+ usage.free_renders_remaining
205
+ usage.spend_remaining_usd
206
+ usage.trial # not None only on a keyless trial
207
+ ```
208
+
209
+ ## Errors
210
+
211
+ Every failure is a `GalleyError` carrying the API's own envelope: a stable `type`, a `docs_url` and,
212
+ for validation, the field path, the expected type, what arrived and a value that would be accepted.
213
+
214
+ ```python
215
+ from galley_render import GalleyError, GalleyConnectionError, GalleyTimeoutError
216
+
217
+ try:
218
+ galley.render("invoice", data={})
219
+ except GalleyError as err:
220
+ err.type # "validation_error"
221
+ err.status # 422
222
+ err.retryable # False
223
+ err.request_id # quote this in a support mail
224
+ for field in err.errors:
225
+ print(field.path, field.message, field.expected, field.received, field.example)
226
+ ```
227
+
228
+ | Type | Status | What to do |
229
+ |---|---|---|
230
+ | `validation_error` | 422 | Fix the named fields. `err.errors` says exactly which. |
231
+ | `invalid_request` | 400 | The request shape is wrong, not the data. |
232
+ | `authentication_error` | 401 | Missing or bad key. |
233
+ | `not_found` | 404 | No such template, version or render on this account. |
234
+ | `quota_exceeded` | 402 | Trial or free tier spent. |
235
+ | `spend_cap_exceeded` | 402 | The account's monthly cap. Raise it in the dashboard. |
236
+ | `rate_limited` | 429 | Retried for you. |
237
+ | `asset_blocked` | 400 | An image or font URL failed the SSRF policy; use a public https URL. |
238
+ | `render_failed` | 500 | The template threw. `err.body` has the detail. |
239
+
240
+ `GalleyConnectionError` means no HTTP response at all — DNS, TLS, timeout. `GalleyTimeoutError`
241
+ means `wait()` gave up while the render was still queued; the render is not lost, so poll again or
242
+ take the webhook.
243
+
244
+ ## Also
245
+
246
+ - [Node SDK](https://www.npmjs.com/package/galley-render) — same surface, zero dependencies.
247
+ - [MCP server](https://mcp.galleyrender.com/mcp) — the same operations as agent tools, no key needed
248
+ to start.
249
+ - [Docs](https://galleyrender.com/docs) · [API reference](https://galleyrender.com/docs/api) ·
250
+ [Template language](https://galleyrender.com/docs/templates)
251
+
252
+ MIT licensed. Support: [support@galleyrender.com](mailto:support@galleyrender.com).
@@ -0,0 +1,216 @@
1
+ # galley-render
2
+
3
+ **JSON in, PDF out.** The official Python client for [Galley Render](https://galleyrender.com) — a
4
+ document API for agents and the programs they write. A template plus a JSON payload becomes a PDF,
5
+ PNG or JPG behind a signed URL, deterministically and cached, so the same input always returns the
6
+ same file and an identical repeat call is free.
7
+
8
+ - Sync and async clients with the same surface.
9
+ - One dependency: `httpx`.
10
+ - Typed responses from the API's own [OpenAPI spec](https://api.galleyrender.com/openapi.json),
11
+ with `.raw` kept intact and `py.typed` shipped.
12
+ - Retries 429 and 5xx with exponential backoff and full jitter.
13
+ - Downloads signed URLs, and re-signs them when they expire.
14
+ - Starts a **50-render keyless trial** with no signup and no card.
15
+
16
+ ```bash
17
+ pip install galley-render
18
+ ```
19
+
20
+ Python 3.9 or newer.
21
+
22
+ ## Quickstart
23
+
24
+ ```python
25
+ import os
26
+ from galley_render import Galley
27
+
28
+ galley = Galley(api_key=os.environ["GALLEY_API_KEY"])
29
+
30
+ render = galley.render(
31
+ "invoice@1",
32
+ format="pdf",
33
+ data={
34
+ "invoice_number": "INV-1042",
35
+ "customer": {"name": "Acme Corp"},
36
+ "line_items": [{"description": "Consulting", "quantity": 12, "unit_price": 150}],
37
+ },
38
+ )
39
+
40
+ print(render.url) # signed, short-lived
41
+ galley.download(render, to_file="invoice.pdf")
42
+ ```
43
+
44
+ Async is the same thing with `await`:
45
+
46
+ ```python
47
+ from galley_render import AsyncGalley
48
+
49
+ async with AsyncGalley() as galley: # reads GALLEY_API_KEY
50
+ render = await galley.render("invoice@1", format="pdf", data=payload)
51
+ pdf = await galley.download(render)
52
+ ```
53
+
54
+ ### No key yet
55
+
56
+ `start_trial()` mints a real 50-render account through Galley's MCP server and hands back a key that
57
+ works everywhere in this package and against the REST API.
58
+
59
+ ```python
60
+ from galley_render import Galley, start_trial
61
+
62
+ trial = start_trial(client_id="my-app") # reuse client_id to keep the same trial
63
+ print(trial.renders_remaining) # 50
64
+
65
+ galley = Galley(api_key=trial.api_key)
66
+ render = galley.render("og-card", format="png", data={"title": "Hello"})
67
+ ```
68
+
69
+ `async_start_trial()` is the awaitable form. To lift the limit, call the `create_account` tool on
70
+ [the MCP server](https://mcp.galleyrender.com/mcp) with an email, or sign up at
71
+ [galleyrender.com](https://galleyrender.com). The trial upgrades in place — nothing it made is lost.
72
+
73
+ ## Configuration
74
+
75
+ ```python
76
+ Galley(
77
+ api_key=None, # default: $GALLEY_API_KEY
78
+ base_url=None, # default: $GALLEY_BASE_URL, then the public API
79
+ timeout=60.0, # seconds, per attempt
80
+ max_retries=3, # extra attempts on 429/5xx and connection failures
81
+ headers=None, # merged into every request
82
+ http_client=None, # bring your own httpx.Client
83
+ )
84
+ ```
85
+
86
+ Both clients are context managers, and close the `httpx` client they created:
87
+
88
+ ```python
89
+ with Galley() as galley:
90
+ ...
91
+ ```
92
+
93
+ ## Rendering
94
+
95
+ ```python
96
+ # Small jobs finish inside the call.
97
+ render = galley.render(
98
+ "certificate@2", # pin the version in anything you ship
99
+ format="pdf", # pdf | png | jpg
100
+ data={"recipient": "Dana Lee", "course": "Rope Access L1"},
101
+ options={"page_size": "Letter", "margin": "18mm", "landscape": True},
102
+ )
103
+
104
+ # A webhook, async_=True or a large payload queues the job instead.
105
+ queued = galley.render("report", data=data, async_=True)
106
+ done = galley.renders.wait(queued.id) # polls with backoff
107
+
108
+ # Or do both in one call, whichever path the API takes.
109
+ finished = galley.render_and_wait("report", data=data)
110
+
111
+ # Up to 50 at a time. A bad item fails alone; the rest still run.
112
+ batch = galley.renders.batch(
113
+ [{"template": "statement@4", "data": c} for c in customers],
114
+ webhook_url="https://example.com/hooks/galley",
115
+ )
116
+ batch.succeeded # the Render objects
117
+ batch.failed # the error envelopes, each with its index
118
+ ```
119
+
120
+ `render.cached` is `True` when the deterministic cache answered: the same template version, data,
121
+ options and format were rendered before, and this call cost nothing.
122
+
123
+ ## Downloading
124
+
125
+ Signed URLs are short-lived; the stored file is not. `download()` takes a render, a render id or a
126
+ URL, and quietly re-signs an expired one.
127
+
128
+ ```python
129
+ data = galley.download(render) # bytes
130
+ galley.download(render, to_file="out/invoice.pdf")
131
+ galley.download("rnd_7hq2m4x8k1bv", to_file="a.png")
132
+ ```
133
+
134
+ ## Templates
135
+
136
+ Templates are code: one self-contained HTML document with inline CSS and Liquid expressions, plus a
137
+ JSON Schema that is the contract for `data`. Versions are immutable and content-addressed.
138
+
139
+ ```python
140
+ galley.templates.list()
141
+ invoice = galley.templates.get("invoice@3")
142
+ invoice.schema # JSON Schema for `data`
143
+ invoice.example # a payload that renders
144
+
145
+ galley.templates.create(
146
+ "welcome-card",
147
+ engine="satori", # fast PNG path for simple flexbox cards
148
+ source="<div style='display:flex'>{{ name }}</div>",
149
+ schema={"type": "object", "required": ["name"], "properties": {"name": {"type": "string"}}},
150
+ example={"name": "Dana"},
151
+ )
152
+
153
+ galley.templates.publish("welcome-card", source="…", message="tighter kerning")
154
+ galley.templates.versions("welcome-card")
155
+
156
+ # Free, renders nothing, and returns the same field errors a render would.
157
+ check = galley.templates.validate("welcome-card", {"name": 42})
158
+ if not check:
159
+ for field in check.errors:
160
+ print(field) # name: must be string (expected string, got number)
161
+ ```
162
+
163
+ ## Usage
164
+
165
+ ```python
166
+ usage = galley.usage()
167
+ usage.billable_units # 1 per PNG/JPG, 1 per PDF page; cache hits are free
168
+ usage.free_renders_remaining
169
+ usage.spend_remaining_usd
170
+ usage.trial # not None only on a keyless trial
171
+ ```
172
+
173
+ ## Errors
174
+
175
+ Every failure is a `GalleyError` carrying the API's own envelope: a stable `type`, a `docs_url` and,
176
+ for validation, the field path, the expected type, what arrived and a value that would be accepted.
177
+
178
+ ```python
179
+ from galley_render import GalleyError, GalleyConnectionError, GalleyTimeoutError
180
+
181
+ try:
182
+ galley.render("invoice", data={})
183
+ except GalleyError as err:
184
+ err.type # "validation_error"
185
+ err.status # 422
186
+ err.retryable # False
187
+ err.request_id # quote this in a support mail
188
+ for field in err.errors:
189
+ print(field.path, field.message, field.expected, field.received, field.example)
190
+ ```
191
+
192
+ | Type | Status | What to do |
193
+ |---|---|---|
194
+ | `validation_error` | 422 | Fix the named fields. `err.errors` says exactly which. |
195
+ | `invalid_request` | 400 | The request shape is wrong, not the data. |
196
+ | `authentication_error` | 401 | Missing or bad key. |
197
+ | `not_found` | 404 | No such template, version or render on this account. |
198
+ | `quota_exceeded` | 402 | Trial or free tier spent. |
199
+ | `spend_cap_exceeded` | 402 | The account's monthly cap. Raise it in the dashboard. |
200
+ | `rate_limited` | 429 | Retried for you. |
201
+ | `asset_blocked` | 400 | An image or font URL failed the SSRF policy; use a public https URL. |
202
+ | `render_failed` | 500 | The template threw. `err.body` has the detail. |
203
+
204
+ `GalleyConnectionError` means no HTTP response at all — DNS, TLS, timeout. `GalleyTimeoutError`
205
+ means `wait()` gave up while the render was still queued; the render is not lost, so poll again or
206
+ take the webhook.
207
+
208
+ ## Also
209
+
210
+ - [Node SDK](https://www.npmjs.com/package/galley-render) — same surface, zero dependencies.
211
+ - [MCP server](https://mcp.galleyrender.com/mcp) — the same operations as agent tools, no key needed
212
+ to start.
213
+ - [Docs](https://galleyrender.com/docs) · [API reference](https://galleyrender.com/docs/api) ·
214
+ [Template language](https://galleyrender.com/docs/templates)
215
+
216
+ MIT licensed. Support: [support@galleyrender.com](mailto:support@galleyrender.com).
@@ -0,0 +1,63 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "galley-render"
7
+ version = "0.1.0"
8
+ description = "Galley Render — JSON in, PDF out. Official Python client for the document API agents can sign themselves up for."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Galley Render", email = "hello@galleyrender.com" }]
14
+ maintainers = [{ name = "Galley Render", email = "support@galleyrender.com" }]
15
+ keywords = [
16
+ "pdf",
17
+ "pdf-generation",
18
+ "html-to-pdf",
19
+ "png",
20
+ "invoice",
21
+ "certificate",
22
+ "og-image",
23
+ "document-api",
24
+ "mcp",
25
+ "agents",
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 4 - Beta",
29
+ "Intended Audience :: Developers",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Programming Language :: Python :: 3.14",
37
+ "Topic :: Software Development :: Libraries :: Python Modules",
38
+ "Topic :: Multimedia :: Graphics :: Graphics Conversion",
39
+ "Topic :: Printing",
40
+ "Typing :: Typed",
41
+ ]
42
+ dependencies = ["httpx>=0.27"]
43
+
44
+ [project.urls]
45
+ Homepage = "https://galleyrender.com"
46
+ Documentation = "https://galleyrender.com/docs/sdks/python"
47
+ Source = "https://github.com/mattmueller/galley"
48
+ Changelog = "https://galleyrender.com/docs/sdks/python/changelog"
49
+ Issues = "https://galleyrender.com/support"
50
+
51
+ [project.optional-dependencies]
52
+ dev = ["pytest>=8", "pytest-asyncio>=0.24", "build>=1.2", "twine>=5"]
53
+
54
+ [tool.hatch.build.targets.wheel]
55
+ packages = ["src/galley_render"]
56
+
57
+ [tool.hatch.build.targets.sdist]
58
+ include = ["src/galley_render", "README.md", "LICENSE", "tests"]
59
+
60
+ [tool.pytest.ini_options]
61
+ testpaths = ["tests"]
62
+ asyncio_mode = "auto"
63
+ filterwarnings = ["error"]
@@ -0,0 +1,57 @@
1
+ """galley-render — JSON in, PDF out.
2
+
3
+ ::
4
+
5
+ from galley_render import Galley
6
+
7
+ galley = Galley(api_key=os.environ["GALLEY_API_KEY"])
8
+ render = galley.render("invoice@1", format="pdf", data={"invoice_number": "INV-1042"})
9
+ galley.download(render, to_file="invoice.pdf")
10
+
11
+ With no key at all, :func:`start_trial` mints a 50-render account through the
12
+ MCP server and hands back a key that works everywhere in this package.
13
+ """
14
+
15
+ from ._client import DEFAULT_BASE_URL, AsyncGalley, Galley, __version__
16
+ from ._errors import (
17
+ FieldError,
18
+ GalleyConnectionError,
19
+ GalleyError,
20
+ GalleyTimeoutError,
21
+ )
22
+ from ._models import (
23
+ Account,
24
+ Batch,
25
+ DeletedTemplate,
26
+ Render,
27
+ Template,
28
+ TemplateVersion,
29
+ Usage,
30
+ Validation,
31
+ WebhookSecret,
32
+ )
33
+ from ._trial import DEFAULT_MCP_URL, Trial, async_start_trial, start_trial
34
+
35
+ __all__ = [
36
+ "Galley",
37
+ "AsyncGalley",
38
+ "start_trial",
39
+ "async_start_trial",
40
+ "Trial",
41
+ "GalleyError",
42
+ "GalleyConnectionError",
43
+ "GalleyTimeoutError",
44
+ "FieldError",
45
+ "Render",
46
+ "Batch",
47
+ "Template",
48
+ "TemplateVersion",
49
+ "Validation",
50
+ "Usage",
51
+ "Account",
52
+ "WebhookSecret",
53
+ "DeletedTemplate",
54
+ "DEFAULT_BASE_URL",
55
+ "DEFAULT_MCP_URL",
56
+ "__version__",
57
+ ]